~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2005-09-06 11:09:03 UTC
  • mfrom: (1178)
  • mto: (1185.1.10) (1092.3.1)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: robertc@robertcollins.net-20050906110903-b6be7bd6102403cb
really merge mpool

Show diffs side-by-side

added added

removed removed

Lines of Context:
220
220
            warn("branch %r was not explicitly unlocked" % self)
221
221
            self._lock.unlock()
222
222
 
223
 
 
224
223
    def lock_write(self):
225
224
        if self._lock_mode:
226
225
            if self._lock_mode != 'w':
406
405
                         """Inventory for the working copy.""")
407
406
 
408
407
 
409
 
    def add(self, files, verbose=False, ids=None):
 
408
    def add(self, files, ids=None):
410
409
        """Make files versioned.
411
410
 
412
 
        Note that the command line normally calls smart_add instead.
 
411
        Note that the command line normally calls smart_add instead,
 
412
        which can automatically recurse.
413
413
 
414
414
        This puts the files in the Added state, so that they will be
415
415
        recorded by the next commit.
425
425
        TODO: Perhaps have an option to add the ids even if the files do
426
426
              not (yet) exist.
427
427
 
428
 
        TODO: Perhaps return the ids of the files?  But then again it
429
 
              is easy to retrieve them if they're needed.
430
 
 
431
 
        TODO: Adding a directory should optionally recurse down and
432
 
              add all non-ignored children.  Perhaps do that in a
433
 
              higher-level method.
 
428
        TODO: Perhaps yield the ids and paths as they're added.
434
429
        """
435
430
        # TODO: Re-adding a file that is removed in the working copy
436
431
        # should probably put it back with the previous ID.
472
467
                    file_id = gen_file_id(f)
473
468
                inv.add_path(f, kind=kind, file_id=file_id)
474
469
 
475
 
                if verbose:
476
 
                    print 'added', quotefn(f)
477
 
 
478
470
                mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
479
471
 
480
472
            self._write_inventory(inv)
885
877
 
886
878
    def lookup_revision(self, revision):
887
879
        """Return the revision identifier for a given revision information."""
888
 
        revno, info = self.get_revision_info(revision)
 
880
        revno, info = self._get_revision_info(revision)
889
881
        return info
890
882
 
891
883
 
906
898
        revision can also be a string, in which case it is parsed for something like
907
899
            'date:' or 'revid:' etc.
908
900
        """
 
901
        revno, rev_id = self._get_revision_info(revision)
 
902
        if revno is None:
 
903
            raise bzrlib.errors.NoSuchRevision(self, revision)
 
904
        return revno, rev_id
 
905
 
 
906
    def get_rev_id(self, revno, history=None):
 
907
        """Find the revision id of the specified revno."""
 
908
        if revno == 0:
 
909
            return None
 
910
        if history is None:
 
911
            history = self.revision_history()
 
912
        elif revno <= 0 or revno > len(history):
 
913
            raise bzrlib.errors.NoSuchRevision(self, revno)
 
914
        return history[revno - 1]
 
915
 
 
916
    def _get_revision_info(self, revision):
 
917
        """Return (revno, revision id) for revision specifier.
 
918
 
 
919
        revision can be an integer, in which case it is assumed to be revno
 
920
        (though this will translate negative values into positive ones)
 
921
        revision can also be a string, in which case it is parsed for something
 
922
        like 'date:' or 'revid:' etc.
 
923
 
 
924
        A revid is always returned.  If it is None, the specifier referred to
 
925
        the null revision.  If the revid does not occur in the revision
 
926
        history, revno will be None.
 
927
        """
 
928
        
909
929
        if revision is None:
910
930
            return 0, None
911
931
        revno = None
915
935
            pass
916
936
        revs = self.revision_history()
917
937
        if isinstance(revision, int):
918
 
            if revision == 0:
919
 
                return 0, None
920
 
            # Mabye we should do this first, but we don't need it if revision == 0
921
938
            if revision < 0:
922
939
                revno = len(revs) + revision + 1
923
940
            else:
924
941
                revno = revision
 
942
            rev_id = self.get_rev_id(revno, revs)
925
943
        elif isinstance(revision, basestring):
926
944
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
927
945
                if revision.startswith(prefix):
928
 
                    revno = func(self, revs, revision)
 
946
                    result = func(self, revs, revision)
 
947
                    if len(result) > 1:
 
948
                        revno, rev_id = result
 
949
                    else:
 
950
                        revno = result[0]
 
951
                        rev_id = self.get_rev_id(revno, revs)
929
952
                    break
930
953
            else:
931
 
                raise BzrError('No namespace registered for string: %r' % revision)
 
954
                raise BzrError('No namespace registered for string: %r' %
 
955
                               revision)
 
956
        else:
 
957
            raise TypeError('Unhandled revision type %s' % revision)
932
958
 
933
 
        if revno is None or revno <= 0 or revno > len(revs):
934
 
            raise BzrError("no such revision %s" % revision)
935
 
        return revno, revs[revno-1]
 
959
        if revno is None:
 
960
            if rev_id is None:
 
961
                raise bzrlib.errors.NoSuchRevision(self, revision)
 
962
        return revno, rev_id
936
963
 
937
964
    def _namespace_revno(self, revs, revision):
938
965
        """Lookup a revision by revision number"""
939
966
        assert revision.startswith('revno:')
940
967
        try:
941
 
            return int(revision[6:])
 
968
            return (int(revision[6:]),)
942
969
        except ValueError:
943
970
            return None
944
971
    REVISION_NAMESPACES['revno:'] = _namespace_revno
945
972
 
946
973
    def _namespace_revid(self, revs, revision):
947
974
        assert revision.startswith('revid:')
 
975
        rev_id = revision[len('revid:'):]
948
976
        try:
949
 
            return revs.index(revision[6:]) + 1
 
977
            return revs.index(rev_id) + 1, rev_id
950
978
        except ValueError:
951
 
            return None
 
979
            return None, rev_id
952
980
    REVISION_NAMESPACES['revid:'] = _namespace_revid
953
981
 
954
982
    def _namespace_last(self, revs, revision):
956
984
        try:
957
985
            offset = int(revision[5:])
958
986
        except ValueError:
959
 
            return None
 
987
            return (None,)
960
988
        else:
961
989
            if offset <= 0:
962
990
                raise BzrError('You must supply a positive value for --revision last:XXX')
963
 
            return len(revs) - offset + 1
 
991
            return (len(revs) - offset + 1,)
964
992
    REVISION_NAMESPACES['last:'] = _namespace_last
965
993
 
966
994
    def _namespace_tag(self, revs, revision):
1041
1069
                # TODO: Handle timezone.
1042
1070
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1043
1071
                if first >= dt and (last is None or dt >= last):
1044
 
                    return i+1
 
1072
                    return (i+1,)
1045
1073
        else:
1046
1074
            for i in range(len(revs)):
1047
1075
                r = self.get_revision(revs[i])
1048
1076
                # TODO: Handle timezone.
1049
1077
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1050
1078
                if first <= dt and (last is None or dt <= last):
1051
 
                    return i+1
 
1079
                    return (i+1,)
1052
1080
    REVISION_NAMESPACES['date:'] = _namespace_date
1053
1081
 
1054
1082
    def revision_tree(self, revision_id):
1119
1147
 
1120
1148
            inv.rename(file_id, to_dir_id, to_tail)
1121
1149
 
1122
 
            print "%s => %s" % (from_rel, to_rel)
1123
 
 
1124
1150
            from_abs = self.abspath(from_rel)
1125
1151
            to_abs = self.abspath(to_rel)
1126
1152
            try:
1145
1171
 
1146
1172
        Note that to_name is only the last component of the new name;
1147
1173
        this doesn't change the directory.
 
1174
 
 
1175
        This returns a list of (from_path, to_path) pairs for each
 
1176
        entry that is moved.
1148
1177
        """
 
1178
        result = []
1149
1179
        self.lock_write()
1150
1180
        try:
1151
1181
            ## TODO: Option to move IDs only
1186
1216
            for f in from_paths:
1187
1217
                name_tail = splitpath(f)[-1]
1188
1218
                dest_path = appendpath(to_name, name_tail)
1189
 
                print "%s => %s" % (f, dest_path)
 
1219
                result.append((f, dest_path))
1190
1220
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1191
1221
                try:
1192
1222
                    os.rename(self.abspath(f), self.abspath(dest_path))
1198
1228
        finally:
1199
1229
            self.unlock()
1200
1230
 
 
1231
        return result
 
1232
 
1201
1233
 
1202
1234
    def revert(self, filenames, old_tree=None, backups=True):
1203
1235
        """Restore selected files to the versions from a previous tree.
1285
1317
            self.unlock()
1286
1318
 
1287
1319
 
 
1320
    def get_parent(self):
 
1321
        """Return the parent location of the branch.
 
1322
 
 
1323
        This is the default location for push/pull/missing.  The usual
 
1324
        pattern is that the user can override it by specifying a
 
1325
        location.
 
1326
        """
 
1327
        import errno
 
1328
        _locs = ['parent', 'pull', 'x-pull']
 
1329
        for l in _locs:
 
1330
            try:
 
1331
                return self.controlfile(l, 'r').read().strip('\n')
 
1332
            except IOError, e:
 
1333
                if e.errno != errno.ENOENT:
 
1334
                    raise
 
1335
        return None
 
1336
 
 
1337
 
 
1338
    def set_parent(self, url):
 
1339
        # TODO: Maybe delete old location files?
 
1340
        from bzrlib.atomicfile import AtomicFile
 
1341
        self.lock_write()
 
1342
        try:
 
1343
            f = AtomicFile(self.controlfilename('parent'))
 
1344
            try:
 
1345
                f.write(url + '\n')
 
1346
                f.commit()
 
1347
            finally:
 
1348
                f.close()
 
1349
        finally:
 
1350
            self.unlock()
 
1351
 
 
1352
    def check_revno(self, revno):
 
1353
        """\
 
1354
        Check whether a revno corresponds to any revision.
 
1355
        Zero (the NULL revision) is considered valid.
 
1356
        """
 
1357
        if revno != 0:
 
1358
            self.check_real_revno(revno)
 
1359
            
 
1360
    def check_real_revno(self, revno):
 
1361
        """\
 
1362
        Check whether a revno corresponds to a real revision.
 
1363
        Zero (the NULL revision) is considered invalid
 
1364
        """
 
1365
        if revno < 1 or revno > self.revno():
 
1366
            raise InvalidRevisionNumber(revno)
 
1367
        
 
1368
        
 
1369
 
1288
1370
 
1289
1371
class ScratchBranch(Branch):
1290
1372
    """Special test class: a branch that cleans up after itself.
1332
1414
        os.rmdir(base)
1333
1415
        copytree(self.base, base, symlinks=True)
1334
1416
        return ScratchBranch(base=base)
 
1417
 
 
1418
 
1335
1419
        
1336
1420
    def __del__(self):
1337
1421
        self.destroy()
1420
1504
def copy_branch(branch_from, to_location, revision=None):
1421
1505
    """Copy branch_from into the existing directory to_location.
1422
1506
 
1423
 
    If revision is not None, the head of the new branch will be revision.
 
1507
    revision
 
1508
        If not None, only revisions up to this point will be copied.
 
1509
        The head of the new branch will be that revision.
 
1510
 
 
1511
    to_location
 
1512
        The name of a local directory that exists but is empty.
1424
1513
    """
1425
1514
    from bzrlib.merge import merge
1426
1515
    from bzrlib.branch import Branch
 
1516
 
 
1517
    assert isinstance(branch_from, Branch)
 
1518
    assert isinstance(to_location, basestring)
 
1519
    
1427
1520
    br_to = Branch(to_location, init=True)
1428
1521
    br_to.set_root_id(branch_from.get_root_id())
1429
1522
    if revision is None:
1433
1526
    br_to.update_revisions(branch_from, stop_revision=revno)
1434
1527
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1435
1528
          check_clean=False, ignore_zero=True)
 
1529
    
1436
1530
    from_location = pull_loc(branch_from)
1437
 
    br_to.controlfile("x-pull", "wb").write(from_location + "\n")
1438
 
 
 
1531
    br_to.set_parent(pull_loc(branch_from))