~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
 
25
25
 
26
26
import bzrlib
27
 
from bzrlib.inventory import InventoryEntry
28
27
import bzrlib.inventory as inventory
29
28
from bzrlib.trace import mutter, note
30
 
from bzrlib.osutils import (isdir, quotefn, compact_date, rand_bytes, 
 
29
from bzrlib.osutils import (isdir, quotefn,
31
30
                            rename, splitpath, sha_file, appendpath, 
32
31
                            file_kind, abspath)
33
32
import bzrlib.errors as errors
237
236
    def set_root_id(self, file_id):
238
237
        raise NotImplementedError('set_root_id is abstract')
239
238
 
240
 
    def add(self, files, ids=None):
241
 
        """Make files versioned.
242
 
 
243
 
        Note that the command line normally calls smart_add instead,
244
 
        which can automatically recurse.
245
 
 
246
 
        This puts the files in the Added state, so that they will be
247
 
        recorded by the next commit.
248
 
 
249
 
        files
250
 
            List of paths to add, relative to the base of the tree.
251
 
 
252
 
        ids
253
 
            If set, use these instead of automatically generated ids.
254
 
            Must be the same length as the list of files, but may
255
 
            contain None for ids that are to be autogenerated.
256
 
 
257
 
        TODO: Perhaps have an option to add the ids even if the files do
258
 
              not (yet) exist.
259
 
 
260
 
        TODO: Perhaps yield the ids and paths as they're added.
261
 
        """
262
 
        raise NotImplementedError('add is abstract')
263
 
 
264
239
    def print_file(self, file, revno):
265
240
        """Print `file` to stdout."""
266
241
        raise NotImplementedError('print_file is abstract')
267
242
 
268
 
    def unknowns(self):
269
 
        """Return all unknown files.
270
 
 
271
 
        These are files in the working directory that are not versioned or
272
 
        control files or ignored.
273
 
        
274
 
        >>> from bzrlib.workingtree import WorkingTree
275
 
        >>> b = ScratchBranch(files=['foo', 'foo~'])
276
 
        >>> map(str, b.unknowns())
277
 
        ['foo']
278
 
        >>> b.add('foo')
279
 
        >>> list(b.unknowns())
280
 
        []
281
 
        >>> WorkingTree(b.base, b).remove('foo')
282
 
        >>> list(b.unknowns())
283
 
        [u'foo']
284
 
        """
285
 
        raise NotImplementedError('unknowns is abstract')
286
 
 
287
243
    def append_revision(self, *revision_ids):
288
244
        raise NotImplementedError('append_revision is abstract')
289
245
 
297
253
        or on the mainline."""
298
254
        raise NotImplementedError('has_revision is abstract')
299
255
 
300
 
    def get_revision_xml_file(self, revision_id):
301
 
        """Return XML file object for revision object."""
302
 
        raise NotImplementedError('get_revision_xml_file is abstract')
303
 
 
304
256
    def get_revision_xml(self, revision_id):
305
257
        raise NotImplementedError('get_revision_xml is abstract')
306
258
 
457
409
        raise NotImplementedError('revision_tree is abstract')
458
410
 
459
411
    def working_tree(self):
460
 
        """Return a `Tree` for the working copy."""
 
412
        """Return a `Tree` for the working copy if this is a local branch."""
461
413
        raise NotImplementedError('working_tree is abstract')
462
414
 
463
415
    def pull(self, source, overwrite=False):
493
445
        """
494
446
        raise NotImplementedError('move is abstract')
495
447
 
496
 
    def revert(self, filenames, old_tree=None, backups=True):
497
 
        """Restore selected files to the versions from a previous tree.
498
 
 
499
 
        backups
500
 
            If true (default) backups are made of files before
501
 
            they're renamed.
502
 
        """
503
 
        raise NotImplementedError('revert is abstract')
504
 
 
505
 
    def pending_merges(self):
506
 
        """Return a list of pending merges.
507
 
 
508
 
        These are revisions that have been merged into the working
509
 
        directory but not yet committed.
510
 
        """
511
 
        raise NotImplementedError('pending_merges is abstract')
512
 
 
513
 
    def add_pending_merge(self, *revision_ids):
514
 
        # TODO: Perhaps should check at this point that the
515
 
        # history of the revision is actually present?
516
 
        raise NotImplementedError('add_pending_merge is abstract')
517
 
 
518
 
    def set_pending_merges(self, rev_list):
519
 
        raise NotImplementedError('set_pending_merges is abstract')
520
 
 
521
448
    def get_parent(self):
522
449
        """Return the parent location of the branch.
523
450
 
658
585
            return ws
659
586
 
660
587
        if self._branch_format == 4:
661
 
            self.inventory_store = get_store('inventory-store')
662
 
            self.text_store = get_store('text-store')
663
 
            self.revision_store = get_store('revision-store')
 
588
            self.inventory_store = get_store(u'inventory-store')
 
589
            self.text_store = get_store(u'text-store')
 
590
            self.revision_store = get_store(u'revision-store')
664
591
        elif self._branch_format == 5:
665
 
            self.control_weaves = get_weave('')
666
 
            self.weave_store = get_weave('weaves')
667
 
            self.revision_store = get_store('revision-store', compressed=False)
 
592
            self.control_weaves = get_weave(u'')
 
593
            self.weave_store = get_weave(u'weaves')
 
594
            self.revision_store = get_store(u'revision-store', compressed=False)
668
595
        elif self._branch_format == 6:
669
 
            self.control_weaves = get_weave('')
670
 
            self.weave_store = get_weave('weaves', prefixed=True)
671
 
            self.revision_store = get_store('revision-store', compressed=False,
 
596
            self.control_weaves = get_weave(u'')
 
597
            self.weave_store = get_weave(u'weaves', prefixed=True)
 
598
            self.revision_store = get_store(u'revision-store', compressed=False,
672
599
                                            prefixed=True)
673
600
        self.revision_store.register_suffix('sig')
674
601
        self._transaction = None
778
705
 
779
706
    def _rel_controlfilename(self, file_or_path):
780
707
        if not isinstance(file_or_path, basestring):
781
 
            file_or_path = '/'.join(file_or_path)
 
708
            file_or_path = u'/'.join(file_or_path)
782
709
        if file_or_path == '':
783
710
            return bzrlib.BZRDIR
784
 
        return bzrlib.transport.urlescape(bzrlib.BZRDIR + '/' + file_or_path)
 
711
        return bzrlib.transport.urlescape(bzrlib.BZRDIR + u'/' + file_or_path)
785
712
 
786
713
    def controlfilename(self, file_or_path):
787
714
        """See Branch.controlfilename."""
890
817
                            'or remove the .bzr directory'
891
818
                            ' and "bzr init" again'])
892
819
 
 
820
    @needs_read_lock
893
821
    def get_root_id(self):
894
822
        """See Branch.get_root_id."""
895
823
        inv = self.get_inventory(self.last_revision())
896
824
        return inv.root.file_id
897
825
 
898
 
    @needs_write_lock
899
 
    def set_root_id(self, file_id):
900
 
        """See Branch.set_root_id."""
901
 
        inv = self.working_tree().read_working_inventory()
902
 
        orig_root_id = inv.root.file_id
903
 
        del inv._byid[inv.root.file_id]
904
 
        inv.root.file_id = file_id
905
 
        inv._byid[inv.root.file_id] = inv.root
906
 
        for fid in inv:
907
 
            entry = inv[fid]
908
 
            if entry.parent_id in (None, orig_root_id):
909
 
                entry.parent_id = inv.root.file_id
910
 
        self._write_inventory(inv)
911
 
 
912
 
    @needs_write_lock
913
 
    def add(self, files, ids=None):
914
 
        """See Branch.add."""
915
 
        # TODO: Re-adding a file that is removed in the working copy
916
 
        # should probably put it back with the previous ID.
917
 
        if isinstance(files, basestring):
918
 
            assert(ids is None or isinstance(ids, basestring))
919
 
            files = [files]
920
 
            if ids is not None:
921
 
                ids = [ids]
922
 
 
923
 
        if ids is None:
924
 
            ids = [None] * len(files)
925
 
        else:
926
 
            assert(len(ids) == len(files))
927
 
 
928
 
        inv = self.working_tree().read_working_inventory()
929
 
        for f,file_id in zip(files, ids):
930
 
            if is_control_file(f):
931
 
                raise BzrError("cannot add control file %s" % quotefn(f))
932
 
 
933
 
            fp = splitpath(f)
934
 
 
935
 
            if len(fp) == 0:
936
 
                raise BzrError("cannot add top-level %r" % f)
937
 
 
938
 
            fullpath = os.path.normpath(self.abspath(f))
939
 
 
940
 
            try:
941
 
                kind = file_kind(fullpath)
942
 
            except OSError:
943
 
                # maybe something better?
944
 
                raise BzrError('cannot add: not a regular file, symlink or directory: %s' % quotefn(f))
945
 
 
946
 
            if not InventoryEntry.versionable_kind(kind):
947
 
                raise BzrError('cannot add: not a versionable file ('
948
 
                               'i.e. regular file, symlink or directory): %s' % quotefn(f))
949
 
 
950
 
            if file_id is None:
951
 
                file_id = gen_file_id(f)
952
 
            inv.add_path(f, kind=kind, file_id=file_id)
953
 
 
954
 
            mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
955
 
 
956
 
        self.working_tree()._write_inventory(inv)
957
 
 
958
826
    @needs_read_lock
959
827
    def print_file(self, file, revno):
960
828
        """See Branch.print_file."""
965
833
            raise BzrError("%r is not present in revision %s" % (file, revno))
966
834
        tree.print_file(file_id)
967
835
 
968
 
    def unknowns(self):
969
 
        """See Branch.unknowns."""
970
 
        return self.working_tree().unknowns()
971
 
 
972
836
    @needs_write_lock
973
837
    def append_revision(self, *revision_ids):
974
838
        """See Branch.append_revision."""
981
845
    @needs_write_lock
982
846
    def set_revision_history(self, rev_history):
983
847
        """See Branch.set_revision_history."""
 
848
        old_revision = self.last_revision()
 
849
        new_revision = rev_history[-1]
984
850
        self.put_controlfile('revision-history', '\n'.join(rev_history))
 
851
        self.working_tree().set_last_revision(new_revision, old_revision)
985
852
 
986
853
    def has_revision(self, revision_id):
987
854
        """See Branch.has_revision."""
989
856
                or self.revision_store.has_id(revision_id))
990
857
 
991
858
    @needs_read_lock
992
 
    def get_revision_xml_file(self, revision_id):
993
 
        """See Branch.get_revision_xml_file."""
 
859
    def _get_revision_xml_file(self, revision_id):
994
860
        if not revision_id or not isinstance(revision_id, basestring):
995
861
            raise InvalidRevisionId(revision_id=revision_id, branch=self)
996
862
        try:
998
864
        except (IndexError, KeyError):
999
865
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
1000
866
 
1001
 
    #deprecated
1002
 
    get_revision_xml = get_revision_xml_file
1003
 
 
1004
867
    def get_revision_xml(self, revision_id):
1005
868
        """See Branch.get_revision_xml."""
1006
 
        return self.get_revision_xml_file(revision_id).read()
1007
 
 
 
869
        return self._get_revision_xml_file(revision_id).read()
1008
870
 
1009
871
    def get_revision(self, revision_id):
1010
872
        """See Branch.get_revision."""
1011
 
        xml_file = self.get_revision_xml_file(revision_id)
 
873
        xml_file = self._get_revision_xml_file(revision_id)
1012
874
 
1013
875
        try:
1014
876
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
1123
985
                else:
1124
986
                    raise e
1125
987
        
1126
 
    def revision_id_to_revno(self, revision_id):
1127
 
        """Given a revision id, return its revno"""
1128
 
        if revision_id is None:
1129
 
            return 0
1130
 
        history = self.revision_history()
1131
 
        try:
1132
 
            return history.index(revision_id) + 1
1133
 
        except ValueError:
1134
 
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
1135
 
 
1136
 
    def get_rev_id(self, revno, history=None):
1137
 
        """Find the revision id of the specified revno."""
1138
 
        if revno == 0:
1139
 
            return None
1140
 
        if history is None:
1141
 
            history = self.revision_history()
1142
 
        elif revno <= 0 or revno > len(history):
1143
 
            raise bzrlib.errors.NoSuchRevision(self, revno)
1144
 
        return history[revno - 1]
1145
 
 
1146
988
    def revision_tree(self, revision_id):
1147
989
        """See Branch.revision_tree."""
1148
990
        # TODO: refactor this to use an existing revision object
1153
995
            inv = self.get_revision_inventory(revision_id)
1154
996
            return RevisionTree(self.weave_store, inv, revision_id)
1155
997
 
 
998
    def basis_tree(self):
 
999
        """See Branch.basis_tree."""
 
1000
        try:
 
1001
            revision_id = self.revision_history()[-1]
 
1002
            xml = self.working_tree().read_basis_inventory(revision_id)
 
1003
            inv = bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
 
1004
            return RevisionTree(self.weave_store, inv, revision_id)
 
1005
        except (IndexError, NoSuchFile), e:
 
1006
            return self.revision_tree(self.last_revision())
 
1007
 
1156
1008
    def working_tree(self):
1157
1009
        """See Branch.working_tree."""
1158
1010
        from bzrlib.workingtree import WorkingTree
1159
 
        # TODO: In the future, perhaps WorkingTree should utilize Transport
1160
 
        # RobertCollins 20051003 - I don't think it should - working trees are
1161
 
        # much more complex to keep consistent than our careful .bzr subset.
1162
 
        # instead, we should say that working trees are local only, and optimise
1163
 
        # for that.
1164
1011
        if self._transport.base.find('://') != -1:
1165
1012
            raise NoWorkingTree(self.base)
1166
1013
        return WorkingTree(self.base, branch=self)
1170
1017
        """See Branch.pull."""
1171
1018
        source.lock_read()
1172
1019
        try:
1173
 
            old_history = self.revision_history()
 
1020
            old_count = len(self.revision_history())
1174
1021
            try:
1175
1022
                self.update_revisions(source)
1176
1023
            except DivergedBranches:
1177
1024
                if not overwrite:
1178
1025
                    raise
1179
1026
                self.set_revision_history(source.revision_history())
1180
 
            new_history = self.revision_history()
 
1027
            new_count = len(self.revision_history())
 
1028
            return new_count - old_count
1181
1029
        finally:
1182
1030
            source.unlock()
1183
1031
 
1184
 
    @needs_write_lock
1185
 
    def rename_one(self, from_rel, to_rel):
1186
 
        """See Branch.rename_one."""
1187
 
        tree = self.working_tree()
1188
 
        inv = tree.inventory
1189
 
        if not tree.has_filename(from_rel):
1190
 
            raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1191
 
        if tree.has_filename(to_rel):
1192
 
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
1193
 
 
1194
 
        file_id = inv.path2id(from_rel)
1195
 
        if file_id == None:
1196
 
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1197
 
 
1198
 
        if inv.path2id(to_rel):
1199
 
            raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1200
 
 
1201
 
        to_dir, to_tail = os.path.split(to_rel)
1202
 
        to_dir_id = inv.path2id(to_dir)
1203
 
        if to_dir_id == None and to_dir != '':
1204
 
            raise BzrError("can't determine destination directory id for %r" % to_dir)
1205
 
 
1206
 
        mutter("rename_one:")
1207
 
        mutter("  file_id    {%s}" % file_id)
1208
 
        mutter("  from_rel   %r" % from_rel)
1209
 
        mutter("  to_rel     %r" % to_rel)
1210
 
        mutter("  to_dir     %r" % to_dir)
1211
 
        mutter("  to_dir_id  {%s}" % to_dir_id)
1212
 
 
1213
 
        inv.rename(file_id, to_dir_id, to_tail)
1214
 
 
1215
 
        from_abs = self.abspath(from_rel)
1216
 
        to_abs = self.abspath(to_rel)
1217
 
        try:
1218
 
            rename(from_abs, to_abs)
1219
 
        except OSError, e:
1220
 
            raise BzrError("failed to rename %r to %r: %s"
1221
 
                    % (from_abs, to_abs, e[1]),
1222
 
                    ["rename rolled back"])
1223
 
 
1224
 
        self.working_tree()._write_inventory(inv)
1225
 
 
1226
 
    @needs_write_lock
1227
 
    def move(self, from_paths, to_name):
1228
 
        """See Branch.move."""
1229
 
        result = []
1230
 
        ## TODO: Option to move IDs only
1231
 
        assert not isinstance(from_paths, basestring)
1232
 
        tree = self.working_tree()
1233
 
        inv = tree.inventory
1234
 
        to_abs = self.abspath(to_name)
1235
 
        if not isdir(to_abs):
1236
 
            raise BzrError("destination %r is not a directory" % to_abs)
1237
 
        if not tree.has_filename(to_name):
1238
 
            raise BzrError("destination %r not in working directory" % to_abs)
1239
 
        to_dir_id = inv.path2id(to_name)
1240
 
        if to_dir_id == None and to_name != '':
1241
 
            raise BzrError("destination %r is not a versioned directory" % to_name)
1242
 
        to_dir_ie = inv[to_dir_id]
1243
 
        if to_dir_ie.kind not in ('directory', 'root_directory'):
1244
 
            raise BzrError("destination %r is not a directory" % to_abs)
1245
 
 
1246
 
        to_idpath = inv.get_idpath(to_dir_id)
1247
 
 
1248
 
        for f in from_paths:
1249
 
            if not tree.has_filename(f):
1250
 
                raise BzrError("%r does not exist in working tree" % f)
1251
 
            f_id = inv.path2id(f)
1252
 
            if f_id == None:
1253
 
                raise BzrError("%r is not versioned" % f)
1254
 
            name_tail = splitpath(f)[-1]
1255
 
            dest_path = appendpath(to_name, name_tail)
1256
 
            if tree.has_filename(dest_path):
1257
 
                raise BzrError("destination %r already exists" % dest_path)
1258
 
            if f_id in to_idpath:
1259
 
                raise BzrError("can't move %r to a subdirectory of itself" % f)
1260
 
 
1261
 
        # OK, so there's a race here, it's possible that someone will
1262
 
        # create a file in this interval and then the rename might be
1263
 
        # left half-done.  But we should have caught most problems.
1264
 
 
1265
 
        for f in from_paths:
1266
 
            name_tail = splitpath(f)[-1]
1267
 
            dest_path = appendpath(to_name, name_tail)
1268
 
            result.append((f, dest_path))
1269
 
            inv.rename(inv.path2id(f), to_dir_id, name_tail)
1270
 
            try:
1271
 
                rename(self.abspath(f), self.abspath(dest_path))
1272
 
            except OSError, e:
1273
 
                raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1274
 
                        ["rename rolled back"])
1275
 
 
1276
 
        self.working_tree()._write_inventory(inv)
1277
 
        return result
1278
 
 
1279
1032
    def get_parent(self):
1280
1033
        """See Branch.get_parent."""
1281
1034
        import errno
1314
1067
    def tree_config(self):
1315
1068
        return TreeConfig(self)
1316
1069
 
1317
 
    def check_revno(self, revno):
1318
 
        """\
1319
 
        Check whether a revno corresponds to any revision.
1320
 
        Zero (the NULL revision) is considered valid.
1321
 
        """
1322
 
        if revno != 0:
1323
 
            self.check_real_revno(revno)
1324
 
            
1325
 
    def check_real_revno(self, revno):
1326
 
        """\
1327
 
        Check whether a revno corresponds to a real revision.
1328
 
        Zero (the NULL revision) is considered invalid
1329
 
        """
1330
 
        if revno < 1 or revno > self.revno():
1331
 
            raise InvalidRevisionNumber(revno)
1332
 
        
1333
1070
    def sign_revision(self, revision_id, gpg_strategy):
1334
1071
        """See Branch.sign_revision."""
1335
1072
        plaintext = Testament.from_revision(self, revision_id).as_short_text()
1412
1149
            break
1413
1150
        filename = head
1414
1151
    return False
1415
 
 
1416
 
 
1417
 
 
1418
 
def gen_file_id(name):
1419
 
    """Return new file id.
1420
 
 
1421
 
    This should probably generate proper UUIDs, but for the moment we
1422
 
    cope with just randomness because running uuidgen every time is
1423
 
    slow."""
1424
 
    import re
1425
 
    from binascii import hexlify
1426
 
    from time import time
1427
 
 
1428
 
    # get last component
1429
 
    idx = name.rfind('/')
1430
 
    if idx != -1:
1431
 
        name = name[idx+1 : ]
1432
 
    idx = name.rfind('\\')
1433
 
    if idx != -1:
1434
 
        name = name[idx+1 : ]
1435
 
 
1436
 
    # make it not a hidden file
1437
 
    name = name.lstrip('.')
1438
 
 
1439
 
    # remove any wierd characters; we don't escape them but rather
1440
 
    # just pull them out
1441
 
    name = re.sub(r'[^\w.]', '', name)
1442
 
 
1443
 
    s = hexlify(rand_bytes(8))
1444
 
    return '-'.join((name, compact_date(time()), s))
1445
 
 
1446
 
 
1447
 
def gen_root_id():
1448
 
    """Return a new tree-root file id."""
1449
 
    return gen_file_id('TREE_ROOT')
1450
 
 
1451