~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: John Arbash Meinel
  • Date: 2005-07-11 18:53:10 UTC
  • mto: (1185.11.1)
  • mto: This revision was merged to the branch mainline in revision 1396.
  • Revision ID: john@arbash-meinel.com-20050711185310-3ed8748ad27e9baf
Working on making Branch() do all of it's work over a Transport.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
 
29
29
 
30
30
def find_branch(f, **args):
31
 
    if f and (f.startswith('http://') or f.startswith('https://')):
32
 
        import remotebranch 
33
 
        return remotebranch.RemoteBranch(f, **args)
34
 
    else:
35
 
        return Branch(f, **args)
36
 
 
 
31
    from transport import transport
 
32
    return Branch(transport(f), **args)
37
33
 
38
34
def find_cached_branch(f, cache_root, **args):
39
35
    from remotebranch import RemoteBranch
154
150
    # This should match a prefix with a function which accepts
155
151
    REVISION_NAMESPACES = {}
156
152
 
157
 
    def __init__(self, base, init=False, find_root=True):
 
153
    def __init__(self, transport, init=False):
158
154
        """Create new branch object at a particular location.
159
155
 
160
 
        base -- Base directory for the branch.
 
156
        transport -- A Transport object, defining how to access files.
 
157
                (If a string, transport.transport() will be used to
 
158
                create a Transport object)
161
159
        
162
160
        init -- If True, create new control files in a previously
163
161
             unversioned directory.  If False, the branch must already
164
162
             be versioned.
165
163
 
166
 
        find_root -- If true and init is false, find the root of the
167
 
             existing branch containing base.
168
 
 
169
164
        In the test suite, creation of new trees is tested using the
170
165
        `ScratchBranch` class.
171
166
        """
172
 
        from bzrlib.store import ImmutableStore
 
167
        from bzrlib.store import CompressedTextStore
 
168
 
 
169
        if isinstance(transport, basestring):
 
170
            from transport import transport as get_transport
 
171
            transport = get_transport(transport)
 
172
 
 
173
        self.transport = transport
173
174
        if init:
174
 
            self.base = os.path.realpath(base)
175
175
            self._make_control()
176
 
        elif find_root:
177
 
            self.base = find_branch_root(base)
178
 
        else:
179
 
            self.base = os.path.realpath(base)
180
 
            if not isdir(self.controlfilename('.')):
181
 
                from errors import NotBranchError
182
 
                raise NotBranchError("not a bzr branch: %s" % quotefn(base),
183
 
                                     ['use "bzr init" to initialize a new working tree',
184
 
                                      'current bzr can only operate from top-of-tree'])
 
176
 
185
177
        self._check_format()
186
178
 
187
 
        self.text_store = ImmutableStore(self.controlfilename('text-store'))
188
 
        self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
189
 
        self.inventory_store = ImmutableStore(self.controlfilename('inventory-store'))
190
 
 
191
179
 
192
180
    def __str__(self):
193
181
        return '%s(%r)' % (self.__class__.__name__, self.base)
205
193
 
206
194
 
207
195
    def lock_write(self):
 
196
        # TODO: Upgrade locking to support using a Transport,
 
197
        # and potentially a remote locking protocol
208
198
        if self._lock_mode:
209
199
            if self._lock_mode != 'w':
210
200
                from errors import LockError
259
249
        return _relpath(self.base, path)
260
250
 
261
251
 
 
252
    def _rel_controlfilename(self, file_or_path):
 
253
        if isinstance(file_or_path, basestring):
 
254
            file_or_path = [file_or_path]
 
255
        return [bzrlib.BZRDIR] + file_or_path
 
256
 
262
257
    def controlfilename(self, file_or_path):
263
258
        """Return location relative to branch."""
264
 
        if isinstance(file_or_path, basestring):
265
 
            file_or_path = [file_or_path]
266
 
        return os.path.join(self.base, bzrlib.BZRDIR, *file_or_path)
 
259
        return self.transport.abspath(self._rel_controlfilename(file_or_path))
267
260
 
268
261
 
269
262
    def controlfile(self, file_or_path, mode='r'):
277
270
        Controlfiles should almost never be opened in write mode but
278
271
        rather should be atomically copied and replaced using atomicfile.
279
272
        """
280
 
 
281
 
        fn = self.controlfilename(file_or_path)
282
 
 
283
 
        if mode == 'rb' or mode == 'wb':
284
 
            return file(fn, mode)
285
 
        elif mode == 'r' or mode == 'w':
286
 
            # open in binary mode anyhow so there's no newline translation;
287
 
            # codecs uses line buffering by default; don't want that.
288
 
            import codecs
289
 
            return codecs.open(fn, mode + 'b', 'utf-8',
290
 
                               buffering=60000)
 
273
        import codecs
 
274
 
 
275
        relpath = self._rel_controlfilename(file_or_path)
 
276
        #TODO: codecs.open() buffers linewise, so it was overloaded with
 
277
        # a much larger buffer, do we need to do the same for getreader/getwriter?
 
278
 
 
279
        # TODO: Try to use transport.put() rather than branch.controlfile(mode='w')
 
280
        if mode == 'rb': 
 
281
            return self.transport.get(relpath)
 
282
        elif mode == 'wb':
 
283
            return self.transport.open(relpath)
 
284
        elif mode == 'r':
 
285
            return codecs.getreader('utf-8')(self.transport.get(relpath))
 
286
        elif mode == 'w':
 
287
            return codecs.getwriter(bzrlib.user_encoding)(
 
288
                    self.transport.open(relpath), errors='replace')
291
289
        else:
292
290
            raise BzrError("invalid controlfile mode %r" % mode)
293
291
 
294
292
 
295
 
 
296
293
    def _make_control(self):
297
294
        from bzrlib.inventory import Inventory
298
295
        from bzrlib.xml import pack_xml
299
296
        
300
 
        os.mkdir(self.controlfilename([]))
301
 
        self.controlfile('README', 'w').write(
 
297
        self.transport.mkdir(self.controlfilename([]))
 
298
        self.transport.put(self._rel_controlfilename('README'),
302
299
            "This is a Bazaar-NG control directory.\n"
303
300
            "Do not change any files in this directory.\n")
304
 
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
 
301
        self.transport.put(self._rel_controlfilename('branch-format'),
 
302
            BZR_BRANCH_FORMAT)
305
303
        for d in ('text-store', 'inventory-store', 'revision-store'):
306
 
            os.mkdir(self.controlfilename(d))
 
304
            self.transport.mkdir(self._rel_controlfilename(d))
307
305
        for f in ('revision-history', 'merged-patches',
308
306
                  'pending-merged-patches', 'branch-name',
309
307
                  'branch-lock',
310
308
                  'pending-merges'):
311
 
            self.controlfile(f, 'w').write('')
 
309
            self.transport.put(self._rel_controlfilename(f), '')
312
310
        mutter('created control directory in ' + self.base)
313
311
 
 
312
        # TODO: Try and do this with self.transport.put() instead
314
313
        pack_xml(Inventory(), self.controlfile('inventory','w'))
315
314
 
316
315
 
332
331
                           ['use a different bzr version',
333
332
                            'or remove the .bzr directory and "bzr init" again'])
334
333
 
 
334
        # We know that the format is the currently supported one.
 
335
        # So create the rest of the entries.
 
336
        def get_store(name):
 
337
            relpath = self._rel_controlfilename(name)
 
338
            return CompressedTextStore(self.transport.clone(relpath))
 
339
 
 
340
        self.text_store = get_store('text-store')
 
341
        self.revision_store = get_store('revision-store')
 
342
        self.inventory_store = get_store('inventory-store')
 
343
 
335
344
 
336
345
 
337
346
    def read_working_inventory(self):
359
368
        That is to say, the inventory describing changes underway, that
360
369
        will be committed to the next revision.
361
370
        """
362
 
        from bzrlib.atomicfile import AtomicFile
363
371
        from bzrlib.xml import pack_xml
364
 
        
 
372
        from cStringIO import StringIO
365
373
        self.lock_write()
366
374
        try:
367
 
            f = AtomicFile(self.controlfilename('inventory'), 'wb')
368
 
            try:
369
 
                pack_xml(inv, f)
370
 
                f.commit()
371
 
            finally:
372
 
                f.close()
 
375
            # Transport handles atomicity
 
376
 
 
377
            sio = StringIO()
 
378
            pack_xml(inv, sio)
 
379
            sio.seek(0)
 
380
            self.transport.put(self._rel_controlfilename('inventory'), sio)
373
381
        finally:
374
382
            self.unlock()
375
383
        
549
557
 
550
558
 
551
559
    def append_revision(self, *revision_ids):
552
 
        from bzrlib.atomicfile import AtomicFile
553
 
 
554
560
        for revision_id in revision_ids:
555
561
            mutter("add {%s} to revision-history" % revision_id)
556
562
 
557
563
        rev_history = self.revision_history()
558
564
        rev_history.extend(revision_ids)
559
565
 
560
 
        f = AtomicFile(self.controlfilename('revision-history'))
 
566
        self.lock_write()
561
567
        try:
562
 
            for rev_id in rev_history:
563
 
                print >>f, rev_id
564
 
            f.commit()
 
568
            self.transport.put(self._rel_controlfilename('revision-history'),
 
569
                    '\n'.join(rev_history))
565
570
        finally:
566
 
            f.close()
 
571
            self.unlock()
567
572
 
568
573
 
569
574
    def get_revision(self, revision_id):
833
838
        revision_ids = [ f.revision_id for f in revisions]
834
839
        count = self.revision_store.copy_multi(other.revision_store, 
835
840
                                               revision_ids)
836
 
        for revision_id in revision_ids:
837
 
            self.append_revision(revision_id)
 
841
        self.append_revision(*revision_ids)
838
842
        print "Added %d revisions." % count
839
843
                    
840
844
        
1201
1205
        These are revisions that have been merged into the working
1202
1206
        directory but not yet committed.
1203
1207
        """
1204
 
        cfn = self.controlfilename('pending-merges')
1205
 
        if not os.path.exists(cfn):
 
1208
        cfn = self._rel_controlfilename('pending-merges')
 
1209
        if not self.transport.has(cfn):
1206
1210
            return []
1207
1211
        p = []
1208
1212
        for l in self.controlfile('pending-merges', 'r').readlines():