~bzr-pqm/bzr/bzr.dev

70 by mbp at sourcefrog
Prepare for smart recursive add.
1
# Copyright (C) 2005 Canonical Ltd
2
1 by mbp at sourcefrog
import from baz patch-364
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
18
import sys
19
import os
1371 by Martin Pool
- raise NotBranchError if format file can't be read
20
import errno
1372 by Martin Pool
- avoid converting inventories to/from StringIO
21
from warnings import warn
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
22
from cStringIO import StringIO
1372 by Martin Pool
- avoid converting inventories to/from StringIO
23
1 by mbp at sourcefrog
import from baz patch-364
24
25
import bzrlib
800 by Martin Pool
Merge John's import-speedup branch:
26
from bzrlib.trace import mutter, note
1390 by Robert Collins
pair programming worx... merge integration and weave
27
from bzrlib.osutils import (isdir, quotefn, compact_date, rand_bytes, 
28
                            rename, splitpath, sha_file, appendpath, 
29
                            file_kind)
1192 by Martin Pool
- clean up code for retrieving stored inventories
30
from bzrlib.errors import (BzrError, InvalidRevisionNumber, InvalidRevisionId,
1299 by Martin Pool
- tidy up imports
31
                           NoSuchRevision, HistoryMissing, NotBranchError,
1391 by Robert Collins
merge from integration
32
                           DivergedBranches, LockError, UnlistableStore,
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
33
                           UnlistableBranch, NoSuchFile)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
34
from bzrlib.textui import show_status
1391 by Robert Collins
merge from integration
35
from bzrlib.revision import Revision, validate_revision_id, is_ancestor
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
36
from bzrlib.delta import compare_trees
37
from bzrlib.tree import EmptyTree, RevisionTree
1192 by Martin Pool
- clean up code for retrieving stored inventories
38
from bzrlib.inventory import Inventory
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
39
from bzrlib.store import copy_all
40
from bzrlib.store.compressed_text import CompressedTextStore
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
41
from bzrlib.store.text import TextStore
1393.2.3 by John Arbash Meinel
Fixing typos, updating stores, getting tests to pass.
42
from bzrlib.store.weave import WeaveStore
1393.2.4 by John Arbash Meinel
All tests pass.
43
from bzrlib.transport import Transport, get_transport
1189 by Martin Pool
- BROKEN: partial support for commit into weave
44
import bzrlib.xml5
1104 by Martin Pool
- Add a simple UIFactory
45
import bzrlib.ui
46
1094 by Martin Pool
- merge aaron's merge improvements 999..1008
47
1186 by Martin Pool
- start implementing v5 format; Branch refuses to operate on old branches
48
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
49
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
1 by mbp at sourcefrog
import from baz patch-364
50
## TODO: Maybe include checks for common corruption of newlines, etc?
51
52
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
53
# TODO: Some operations like log might retrieve the same revisions
54
# repeatedly to calculate deltas.  We could perhaps have a weakref
1223 by Martin Pool
- store inventories in weave
55
# cache in memory to make this faster.  In general anything can be
56
# cached in memory between lock and unlock operations.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
57
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
58
def find_branch(*ignored, **ignored_too):
59
    # XXX: leave this here for about one release, then remove it
60
    raise NotImplementedError('find_branch() is not supported anymore, '
61
                              'please use one of the new branch constructors')
600 by Martin Pool
- Better Branch.relpath that doesn't match on
62
def _relpath(base, path):
63
    """Return path relative to base, or raise exception.
64
65
    The path may be either an absolute path or a path relative to the
66
    current working directory.
67
68
    Lifted out of Branch.relpath for ease of testing.
69
70
    os.path.commonprefix (python2.4) has a bad bug that it works just
71
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
72
    avoids that problem."""
73
    rp = os.path.abspath(path)
74
75
    s = []
76
    head = rp
77
    while len(head) >= len(base):
78
        if head == base:
79
            break
80
        head, tail = os.path.split(head)
81
        if tail:
82
            s.insert(0, tail)
83
    else:
84
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
85
86
    return os.sep.join(s)
416 by Martin Pool
- bzr log and bzr root now accept an http URL
87
        
88
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
89
def find_branch_root(t):
90
    """Find the branch root enclosing the transport's base.
91
92
    t is a Transport object.
93
94
    It is not necessary that the base of t exists.
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
95
96
    Basically we keep looking up until we find the control directory or
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
97
    run into the root.  If there isn't one, raises NotBranchError.
98
    """
1185.11.9 by John Arbash Meinel
Most tests pass, some problems with unavailable socket recv
99
    orig_base = t.base
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
100
    while True:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
101
        if t.has(bzrlib.BZRDIR):
102
            return t
103
        new_t = t.clone('..')
104
        if new_t.base == t.base:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
105
            # reached the root, whatever that may be
1185.11.9 by John Arbash Meinel
Most tests pass, some problems with unavailable socket recv
106
            raise NotBranchError('%s is not in a branch' % orig_base)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
107
        t = new_t
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
108
685 by Martin Pool
- add -r option to the branch command
109
1 by mbp at sourcefrog
import from baz patch-364
110
######################################################################
111
# branch objects
112
558 by Martin Pool
- All top-level classes inherit from object
113
class Branch(object):
1 by mbp at sourcefrog
import from baz patch-364
114
    """Branch holding a history of revisions.
115
343 by Martin Pool
doc
116
    base
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
117
        Base directory/url of the branch.
118
    """
119
    base = None
120
121
    def __init__(self, *ignored, **ignored_too):
122
        raise NotImplementedError('The Branch class is abstract')
123
124
    @staticmethod
1393.1.2 by Martin Pool
- better representation in Branch factories of opening old formats
125
    def open_downlevel(base):
126
        """Open a branch which may be of an old format.
127
        
128
        Only local branches are supported."""
1393.1.16 by Martin Pool
- fix up Branch.open_downlevel for Transport
129
        return _Branch(get_transport(base), relax_version_check=True)
1393.1.2 by Martin Pool
- better representation in Branch factories of opening old formats
130
        
131
    @staticmethod
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
132
    def open(base):
133
        """Open an existing branch, rooted at 'base' (url)"""
1393.2.4 by John Arbash Meinel
All tests pass.
134
        t = get_transport(base)
1185.11.11 by John Arbash Meinel
Removing the remotebranch object.
135
        return _Branch(t)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
136
137
    @staticmethod
1185.2.8 by Lalo Martins
creating the new branch constructors
138
    def open_containing(url):
1185.1.41 by Robert Collins
massive patch from Alexander Belchenko - many PEP8 fixes, removes unused function uuid
139
        """Open an existing branch which contains url.
140
        
141
        This probes for a branch at url, and searches upwards from there.
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
142
        """
1393.2.4 by John Arbash Meinel
All tests pass.
143
        t = get_transport(url)
1185.11.9 by John Arbash Meinel
Most tests pass, some problems with unavailable socket recv
144
        t = find_branch_root(t)
1185.11.11 by John Arbash Meinel
Removing the remotebranch object.
145
        return _Branch(t)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
146
147
    @staticmethod
148
    def initialize(base):
149
        """Create a new branch, rooted at 'base' (url)"""
1393.2.4 by John Arbash Meinel
All tests pass.
150
        t = get_transport(base)
1185.11.11 by John Arbash Meinel
Removing the remotebranch object.
151
        return _Branch(t, init=True)
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
152
153
    def setup_caching(self, cache_root):
154
        """Subclasses that care about caching should override this, and set
155
        up cached stores located under cache_root.
156
        """
157
158
1185.11.11 by John Arbash Meinel
Removing the remotebranch object.
159
class _Branch(Branch):
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
160
    """A branch stored in the actual filesystem.
161
162
    Note that it's "local" in the context of the filesystem; it doesn't
163
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
164
    it's writable, and can be accessed via the normal filesystem API.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
165
166
    _lock_mode
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
167
        None, or 'r' or 'w'
168
169
    _lock_count
170
        If _lock_mode is true, a positive count of the number of times the
171
        lock has been taken.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
172
614 by Martin Pool
- unify two defintions of LockError
173
    _lock
174
        Lock object from bzrlib.lock.
1 by mbp at sourcefrog
import from baz patch-364
175
    """
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
176
    # We actually expect this class to be somewhat short-lived; part of its
177
    # purpose is to try to isolate what bits of the branch logic are tied to
178
    # filesystem access, so that in a later step, we can extricate them to
179
    # a separarte ("storage") class.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
180
    _lock_mode = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
181
    _lock_count = None
615 by Martin Pool
Major rework of locking code:
182
    _lock = None
1223 by Martin Pool
- store inventories in weave
183
    _inventory_weave = None
353 by Martin Pool
- Per-branch locks in read and write modes.
184
    
897 by Martin Pool
- merge john's revision-naming code
185
    # Map some sort of prefix into a namespace
186
    # stuff like "revno:10", "revid:", etc.
187
    # This should match a prefix with a function which accepts
188
    REVISION_NAMESPACES = {}
189
1391 by Robert Collins
merge from integration
190
    def push_stores(self, branch_to):
191
        """Copy the content of this branches store to branch_to."""
192
        if (self._branch_format != branch_to._branch_format
193
            or self._branch_format != 4):
194
            from bzrlib.fetch import greedy_fetch
1393 by Robert Collins
reenable remotebranch tests
195
            mutter("falling back to fetch logic to push between %s(%s) and %s(%s)",
196
                   self, self._branch_format, branch_to, branch_to._branch_format)
1391 by Robert Collins
merge from integration
197
            greedy_fetch(to_branch=branch_to, from_branch=self,
198
                         revision=self.last_revision())
199
            return
200
201
        store_pairs = ((self.text_store,      branch_to.text_store),
202
                       (self.inventory_store, branch_to.inventory_store),
203
                       (self.revision_store,  branch_to.revision_store))
204
        try:
205
            for from_store, to_store in store_pairs: 
206
                copy_all(from_store, to_store)
207
        except UnlistableStore:
208
            raise UnlistableBranch(from_store)
209
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
210
    def __init__(self, transport, init=False,
1293 by Martin Pool
- add Branch constructor option to relax version check
211
                 relax_version_check=False):
1 by mbp at sourcefrog
import from baz patch-364
212
        """Create new branch object at a particular location.
213
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
214
        transport -- A Transport object, defining how to access files.
215
                (If a string, transport.transport() will be used to
216
                create a Transport object)
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
217
        
254 by Martin Pool
- Doc cleanups from Magnus Therning
218
        init -- If True, create new control files in a previously
1 by mbp at sourcefrog
import from baz patch-364
219
             unversioned directory.  If False, the branch must already
220
             be versioned.
221
1293 by Martin Pool
- add Branch constructor option to relax version check
222
        relax_version_check -- If true, the usual check for the branch
223
            version is not applied.  This is intended only for
224
            upgrade/recovery type use; it's not guaranteed that
225
            all operations will work on old format branches.
226
1 by mbp at sourcefrog
import from baz patch-364
227
        In the test suite, creation of new trees is tested using the
228
        `ScratchBranch` class.
229
        """
1393.1.15 by Martin Pool
- better assertion message
230
        assert isinstance(transport, Transport), \
231
            "%r is not a Transport" % transport
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
232
        self._transport = transport
1 by mbp at sourcefrog
import from baz patch-364
233
        if init:
234
            self._make_control()
1293 by Martin Pool
- add Branch constructor option to relax version check
235
        self._check_format(relax_version_check)
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
236
237
        def get_store(name, compressed=True):
238
            relpath = self._rel_controlfilename(name)
239
            if compressed:
240
                store = CompressedTextStore(self._transport.clone(relpath))
241
            else:
242
                store = TextStore(self._transport.clone(relpath))
243
            if self._transport.should_cache():
244
                from meta_store import CachedStore
245
                cache_path = os.path.join(self.cache_root, name)
246
                os.mkdir(cache_path)
247
                store = CachedStore(store, cache_path)
248
            return store
249
        def get_weave(name):
250
            relpath = self._rel_controlfilename(name)
251
            ws = WeaveStore(self._transport.clone(relpath))
252
            if self._transport.should_cache():
253
                ws.enable_cache = True
254
            return ws
255
1296 by Martin Pool
- v4 branch should allow access to inventory and text stores
256
        if self._branch_format == 4:
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
257
            self.inventory_store = get_store('inventory-store')
258
            self.text_store = get_store('text-store')
259
            self.revision_store = get_store('revision-store')
1390 by Robert Collins
pair programming worx... merge integration and weave
260
        elif self._branch_format == 5:
1393.2.3 by John Arbash Meinel
Fixing typos, updating stores, getting tests to pass.
261
            self.control_weaves = get_weave([])
262
            self.weave_store = get_weave('weaves')
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
263
            self.revision_store = get_store('revision-store', compressed=False)
1 by mbp at sourcefrog
import from baz patch-364
264
265
    def __str__(self):
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
266
        return '%s(%r)' % (self.__class__.__name__, self._transport.base)
1 by mbp at sourcefrog
import from baz patch-364
267
268
269
    __repr__ = __str__
270
271
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
272
    def __del__(self):
615 by Martin Pool
Major rework of locking code:
273
        if self._lock_mode or self._lock:
1390 by Robert Collins
pair programming worx... merge integration and weave
274
            # XXX: This should show something every time, and be suitable for
275
            # headless operation and embedding
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
276
            warn("branch %r was not explicitly unlocked" % self)
615 by Martin Pool
Major rework of locking code:
277
            self._lock.unlock()
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
278
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
279
        # TODO: It might be best to do this somewhere else,
280
        # but it is nice for a Branch object to automatically
281
        # cache it's information.
282
        # Alternatively, we could have the Transport objects cache requests
283
        # See the earlier discussion about how major objects (like Branch)
284
        # should never expect their __del__ function to run.
1185.11.9 by John Arbash Meinel
Most tests pass, some problems with unavailable socket recv
285
        if hasattr(self, 'cache_root') and self.cache_root is not None:
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
286
            try:
287
                import shutil
288
                shutil.rmtree(self.cache_root)
289
            except:
290
                pass
291
            self.cache_root = None
292
907.1.17 by John Arbash Meinel
Adding a Branch.base property, removing pull_loc()
293
    def _get_base(self):
907.1.19 by John Arbash Meinel
Updated ScratchBranch and Branch.base, All Tests PASS !!!
294
        if self._transport:
295
            return self._transport.base
296
        return None
907.1.17 by John Arbash Meinel
Adding a Branch.base property, removing pull_loc()
297
298
    base = property(_get_base)
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
299
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
300
301
    def lock_write(self):
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
302
        # TODO: Upgrade locking to support using a Transport,
303
        # and potentially a remote locking protocol
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
304
        if self._lock_mode:
305
            if self._lock_mode != 'w':
306
                raise LockError("can't upgrade to a write lock from %r" %
307
                                self._lock_mode)
308
            self._lock_count += 1
309
        else:
907.1.24 by John Arbash Meinel
Remote functionality work.
310
            self._lock = self._transport.lock_write(
311
                    self._rel_controlfilename('branch-lock'))
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
312
            self._lock_mode = 'w'
313
            self._lock_count = 1
314
315
316
    def lock_read(self):
317
        if self._lock_mode:
318
            assert self._lock_mode in ('r', 'w'), \
319
                   "invalid lock mode %r" % self._lock_mode
320
            self._lock_count += 1
321
        else:
907.1.24 by John Arbash Meinel
Remote functionality work.
322
            self._lock = self._transport.lock_read(
323
                    self._rel_controlfilename('branch-lock'))
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
324
            self._lock_mode = 'r'
325
            self._lock_count = 1
326
                        
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
327
    def unlock(self):
328
        if not self._lock_mode:
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
329
            raise LockError('branch %r is not locked' % (self))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
330
331
        if self._lock_count > 1:
332
            self._lock_count -= 1
333
        else:
615 by Martin Pool
Major rework of locking code:
334
            self._lock.unlock()
335
            self._lock = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
336
            self._lock_mode = self._lock_count = None
353 by Martin Pool
- Per-branch locks in read and write modes.
337
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
338
    def abspath(self, name):
339
        """Return absolute filename for something in the branch"""
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
340
        return self._transport.abspath(name)
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
341
68 by mbp at sourcefrog
- new relpath command and function
342
    def relpath(self, path):
343
        """Return path relative to this branch of something inside it.
344
345
        Raises an error if path is not in this branch."""
907.1.24 by John Arbash Meinel
Remote functionality work.
346
        return self._transport.relpath(path)
68 by mbp at sourcefrog
- new relpath command and function
347
348
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
349
    def _rel_controlfilename(self, file_or_path):
350
        if isinstance(file_or_path, basestring):
351
            file_or_path = [file_or_path]
352
        return [bzrlib.BZRDIR] + file_or_path
353
1 by mbp at sourcefrog
import from baz patch-364
354
    def controlfilename(self, file_or_path):
355
        """Return location relative to branch."""
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
356
        return self._transport.abspath(self._rel_controlfilename(file_or_path))
1 by mbp at sourcefrog
import from baz patch-364
357
358
359
    def controlfile(self, file_or_path, mode='r'):
245 by mbp at sourcefrog
- control files always in utf-8-unix format
360
        """Open a control file for this branch.
361
362
        There are two classes of file in the control directory: text
363
        and binary.  binary files are untranslated byte streams.  Text
364
        control files are stored with Unix newlines and in UTF-8, even
365
        if the platform or locale defaults are different.
430 by Martin Pool
doc
366
367
        Controlfiles should almost never be opened in write mode but
368
        rather should be atomically copied and replaced using atomicfile.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
369
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
370
        import codecs
371
372
        relpath = self._rel_controlfilename(file_or_path)
373
        #TODO: codecs.open() buffers linewise, so it was overloaded with
374
        # a much larger buffer, do we need to do the same for getreader/getwriter?
375
        if mode == 'rb': 
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
376
            return self._transport.get(relpath)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
377
        elif mode == 'wb':
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
378
            raise BzrError("Branch.controlfile(mode='wb') is not supported, use put_controlfiles")
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
379
        elif mode == 'r':
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
380
            return codecs.getreader('utf-8')(self._transport.get(relpath), errors='replace')
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
381
        elif mode == 'w':
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
382
            raise BzrError("Branch.controlfile(mode='w') is not supported, use put_controlfiles")
245 by mbp at sourcefrog
- control files always in utf-8-unix format
383
        else:
384
            raise BzrError("invalid controlfile mode %r" % mode)
385
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
386
    def put_controlfile(self, path, f, encode=True):
907.1.20 by John Arbash Meinel
Removed Transport.open(), making get + put encode/decode to utf-8
387
        """Write an entry as a controlfile.
388
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
389
        :param path: The path to put the file, relative to the .bzr control
390
                     directory
391
        :param f: A file-like or string object whose contents should be copied.
392
        :param encode:  If true, encode the contents as utf-8
393
        """
394
        self.put_controlfiles([(path, f)], encode=encode)
395
396
    def put_controlfiles(self, files, encode=True):
397
        """Write several entries as controlfiles.
398
399
        :param files: A list of [(path, file)] pairs, where the path is the directory
400
                      underneath the bzr control directory
401
        :param encode:  If true, encode the contents as utf-8
402
        """
403
        import codecs
404
        ctrl_files = []
405
        for path, f in files:
406
            if encode:
407
                if isinstance(f, basestring):
408
                    f = f.encode('utf-8', 'replace')
409
                else:
410
                    f = codecs.getwriter('utf-8')(f, errors='replace')
411
            path = self._rel_controlfilename(path)
412
            ctrl_files.append((path, f))
413
        self._transport.put_multi(ctrl_files)
1 by mbp at sourcefrog
import from baz patch-364
414
415
    def _make_control(self):
800 by Martin Pool
Merge John's import-speedup branch:
416
        from bzrlib.inventory import Inventory
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
417
        from bzrlib.weavefile import write_weave_v5
418
        from bzrlib.weave import Weave
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
419
        
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
420
        # Create an empty inventory
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
421
        sio = StringIO()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
422
        # if we want per-tree root ids then this is the place to set
423
        # them; they're not needed for now and so ommitted for
424
        # simplicity.
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
425
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), sio)
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
426
        empty_inv = sio.getvalue()
427
        sio = StringIO()
428
        bzrlib.weavefile.write_weave_v5(Weave(), sio)
429
        empty_weave = sio.getvalue()
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
430
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
431
        dirs = [[], 'revision-store', 'weaves']
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
432
        files = [('README', 
1 by mbp at sourcefrog
import from baz patch-364
433
            "This is a Bazaar-NG control directory.\n"
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
434
            "Do not change any files in this directory.\n"),
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
435
            ('branch-format', BZR_BRANCH_FORMAT_5),
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
436
            ('revision-history', ''),
437
            ('branch-name', ''),
438
            ('branch-lock', ''),
439
            ('pending-merges', ''),
1393.2.2 by John Arbash Meinel
Updated stores to use Transport
440
            ('inventory', empty_inv),
441
            ('inventory.weave', empty_weave),
442
            ('ancestry.weave', empty_weave)
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
443
        ]
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
444
        cfn = self._rel_controlfilename
445
        self._transport.mkdir_multi([cfn(d) for d in dirs])
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
446
        self.put_controlfiles(files)
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
447
        mutter('created control directory in ' + self._transport.base)
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
448
1293 by Martin Pool
- add Branch constructor option to relax version check
449
    def _check_format(self, relax_version_check):
1 by mbp at sourcefrog
import from baz patch-364
450
        """Check this branch format is supported.
451
1187 by Martin Pool
- improved check for branch version
452
        The format level is stored, as an integer, in
453
        self._branch_format for code that needs to check it later.
1 by mbp at sourcefrog
import from baz patch-364
454
455
        In the future, we might need different in-memory Branch
456
        classes to support downlevel branches.  But not yet.
163 by mbp at sourcefrog
merge win32 portability fixes
457
        """
1185.11.9 by John Arbash Meinel
Most tests pass, some problems with unavailable socket recv
458
        try:
459
            fmt = self.controlfile('branch-format', 'r').read()
460
        except NoSuchFile:
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
461
            raise NotBranchError(self.base)
1371 by Martin Pool
- raise NotBranchError if format file can't be read
462
1187 by Martin Pool
- improved check for branch version
463
        if fmt == BZR_BRANCH_FORMAT_5:
464
            self._branch_format = 5
1294 by Martin Pool
- refactor branch version detection
465
        elif fmt == BZR_BRANCH_FORMAT_4:
466
            self._branch_format = 4
467
468
        if (not relax_version_check
469
            and self._branch_format != 5):
576 by Martin Pool
- raise exceptions rather than using bailout()
470
            raise BzrError('sorry, branch format %r not supported' % fmt,
471
                           ['use a different bzr version',
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
472
                            'or remove the .bzr directory'
473
                            ' and "bzr init" again'])
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
474
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
475
    def get_root_id(self):
476
        """Return the id of this branches root"""
477
        inv = self.read_working_inventory()
478
        return inv.root.file_id
1 by mbp at sourcefrog
import from baz patch-364
479
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
480
    def set_root_id(self, file_id):
481
        inv = self.read_working_inventory()
482
        orig_root_id = inv.root.file_id
483
        del inv._byid[inv.root.file_id]
484
        inv.root.file_id = file_id
485
        inv._byid[inv.root.file_id] = inv.root
486
        for fid in inv:
487
            entry = inv[fid]
488
            if entry.parent_id in (None, orig_root_id):
489
                entry.parent_id = inv.root.file_id
490
        self._write_inventory(inv)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
491
1 by mbp at sourcefrog
import from baz patch-364
492
    def read_working_inventory(self):
493
        """Read the working inventory."""
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
494
        self.lock_read()
495
        try:
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
496
            # ElementTree does its own conversion from UTF-8, so open in
497
            # binary.
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
498
            f = self.controlfile('inventory', 'rb')
1189 by Martin Pool
- BROKEN: partial support for commit into weave
499
            return bzrlib.xml5.serializer_v5.read_inventory(f)
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
500
        finally:
501
            self.unlock()
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
502
            
1 by mbp at sourcefrog
import from baz patch-364
503
504
    def _write_inventory(self, inv):
505
        """Update the working inventory.
506
507
        That is to say, the inventory describing changes underway, that
508
        will be committed to the next revision.
509
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
510
        from cStringIO import StringIO
770 by Martin Pool
- write new working inventory using AtomicFile
511
        self.lock_write()
512
        try:
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
513
            sio = StringIO()
1393.2.1 by John Arbash Meinel
Merged in split-storage-2 branch. Need to cleanup a little bit more still.
514
            bzrlib.xml5.serializer_v5.write_inventory(inv, sio)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
515
            sio.seek(0)
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
516
            # Transport handles atomicity
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
517
            self.put_controlfile('inventory', sio)
770 by Martin Pool
- write new working inventory using AtomicFile
518
        finally:
519
            self.unlock()
520
        
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
521
        mutter('wrote working inventory')
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
522
            
1 by mbp at sourcefrog
import from baz patch-364
523
524
    inventory = property(read_working_inventory, _write_inventory, None,
525
                         """Inventory for the working copy.""")
526
527
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
528
    def add(self, files, ids=None):
1 by mbp at sourcefrog
import from baz patch-364
529
        """Make files versioned.
530
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
531
        Note that the command line normally calls smart_add instead,
532
        which can automatically recurse.
247 by mbp at sourcefrog
doc
533
1 by mbp at sourcefrog
import from baz patch-364
534
        This puts the files in the Added state, so that they will be
535
        recorded by the next commit.
536
596 by Martin Pool
doc
537
        files
538
            List of paths to add, relative to the base of the tree.
539
540
        ids
541
            If set, use these instead of automatically generated ids.
542
            Must be the same length as the list of files, but may
543
            contain None for ids that are to be autogenerated.
544
254 by Martin Pool
- Doc cleanups from Magnus Therning
545
        TODO: Perhaps have an option to add the ids even if the files do
596 by Martin Pool
doc
546
              not (yet) exist.
1 by mbp at sourcefrog
import from baz patch-364
547
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
548
        TODO: Perhaps yield the ids and paths as they're added.
1 by mbp at sourcefrog
import from baz patch-364
549
        """
550
        # TODO: Re-adding a file that is removed in the working copy
551
        # should probably put it back with the previous ID.
800 by Martin Pool
Merge John's import-speedup branch:
552
        if isinstance(files, basestring):
553
            assert(ids is None or isinstance(ids, basestring))
1 by mbp at sourcefrog
import from baz patch-364
554
            files = [files]
493 by Martin Pool
- Merge aaron's merge command
555
            if ids is not None:
556
                ids = [ids]
557
558
        if ids is None:
559
            ids = [None] * len(files)
560
        else:
561
            assert(len(ids) == len(files))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
562
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
563
        self.lock_write()
564
        try:
565
            inv = self.read_working_inventory()
566
            for f,file_id in zip(files, ids):
567
                if is_control_file(f):
568
                    raise BzrError("cannot add control file %s" % quotefn(f))
569
570
                fp = splitpath(f)
571
572
                if len(fp) == 0:
573
                    raise BzrError("cannot add top-level %r" % f)
574
575
                fullpath = os.path.normpath(self.abspath(f))
576
577
                try:
578
                    kind = file_kind(fullpath)
579
                except OSError:
580
                    # maybe something better?
581
                    raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
582
583
                if kind != 'file' and kind != 'directory':
584
                    raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
585
586
                if file_id is None:
587
                    file_id = gen_file_id(f)
588
                inv.add_path(f, kind=kind, file_id=file_id)
589
590
                mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
591
592
            self._write_inventory(inv)
593
        finally:
594
            self.unlock()
70 by mbp at sourcefrog
Prepare for smart recursive add.
595
            
1 by mbp at sourcefrog
import from baz patch-364
596
176 by mbp at sourcefrog
New cat command contributed by janmar.
597
    def print_file(self, file, revno):
598
        """Print `file` to stdout."""
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
599
        self.lock_read()
600
        try:
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
601
            tree = self.revision_tree(self.get_rev_id(revno))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
602
            # use inventory as it was in that revision
603
            file_id = tree.inventory.path2id(file)
604
            if not file_id:
897 by Martin Pool
- merge john's revision-naming code
605
                raise BzrError("%r is not present in revision %s" % (file, revno))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
606
            tree.print_file(file_id)
607
        finally:
608
            self.unlock()
609
610
1 by mbp at sourcefrog
import from baz patch-364
611
    def remove(self, files, verbose=False):
612
        """Mark nominated files for removal from the inventory.
613
614
        This does not remove their text.  This does not run on 
615
254 by Martin Pool
- Doc cleanups from Magnus Therning
616
        TODO: Refuse to remove modified files unless --force is given?
1 by mbp at sourcefrog
import from baz patch-364
617
254 by Martin Pool
- Doc cleanups from Magnus Therning
618
        TODO: Do something useful with directories.
1 by mbp at sourcefrog
import from baz patch-364
619
254 by Martin Pool
- Doc cleanups from Magnus Therning
620
        TODO: Should this remove the text or not?  Tough call; not
1 by mbp at sourcefrog
import from baz patch-364
621
        removing may be useful and the user can just use use rm, and
622
        is the opposite of add.  Removing it is consistent with most
623
        other tools.  Maybe an option.
624
        """
625
        ## TODO: Normalize names
626
        ## TODO: Remove nested loops; better scalability
800 by Martin Pool
Merge John's import-speedup branch:
627
        if isinstance(files, basestring):
1 by mbp at sourcefrog
import from baz patch-364
628
            files = [files]
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
629
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
630
        self.lock_write()
631
632
        try:
633
            tree = self.working_tree()
634
            inv = tree.inventory
635
636
            # do this before any modifications
637
            for f in files:
638
                fid = inv.path2id(f)
639
                if not fid:
640
                    raise BzrError("cannot remove unversioned file %s" % quotefn(f))
641
                mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
642
                if verbose:
643
                    # having remove it, it must be either ignored or unknown
644
                    if tree.is_ignored(f):
645
                        new_status = 'I'
646
                    else:
647
                        new_status = '?'
648
                    show_status(new_status, inv[fid].kind, quotefn(f))
649
                del inv[fid]
650
651
            self._write_inventory(inv)
652
        finally:
653
            self.unlock()
654
655
612 by Martin Pool
doc
656
    # FIXME: this doesn't need to be a branch method
493 by Martin Pool
- Merge aaron's merge command
657
    def set_inventory(self, new_inventory_list):
800 by Martin Pool
Merge John's import-speedup branch:
658
        from bzrlib.inventory import Inventory, InventoryEntry
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
659
        inv = Inventory(self.get_root_id())
493 by Martin Pool
- Merge aaron's merge command
660
        for path, file_id, parent, kind in new_inventory_list:
661
            name = os.path.basename(path)
662
            if name == "":
663
                continue
664
            inv.add(InventoryEntry(file_id, name, kind, parent))
665
        self._write_inventory(inv)
666
1 by mbp at sourcefrog
import from baz patch-364
667
668
    def unknowns(self):
669
        """Return all unknown files.
670
671
        These are files in the working directory that are not versioned or
672
        control files or ignored.
673
        
674
        >>> b = ScratchBranch(files=['foo', 'foo~'])
675
        >>> list(b.unknowns())
676
        ['foo']
677
        >>> b.add('foo')
678
        >>> list(b.unknowns())
679
        []
680
        >>> b.remove('foo')
681
        >>> list(b.unknowns())
682
        ['foo']
683
        """
684
        return self.working_tree().unknowns()
685
686
905 by Martin Pool
- merge aaron's append_multiple.patch
687
    def append_revision(self, *revision_ids):
688
        for revision_id in revision_ids:
689
            mutter("add {%s} to revision-history" % revision_id)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
690
        self.lock_write()
769 by Martin Pool
- append to branch revision history using AtomicFile
691
        try:
1393.1.22 by Martin Pool
- clean up Branch.append_revision()
692
            rev_history = self.revision_history()
693
            rev_history.extend(revision_ids)
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
694
            self.put_controlfile('revision-history', '\n'.join(rev_history))
769 by Martin Pool
- append to branch revision history using AtomicFile
695
        finally:
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
696
            self.unlock()
233 by mbp at sourcefrog
- more output from test.sh
697
698
1261 by Martin Pool
- new method Branch.has_revision
699
    def has_revision(self, revision_id):
700
        """True if this branch has a copy of the revision.
701
702
        This does not necessarily imply the revision is merge
703
        or on the mainline."""
1390 by Robert Collins
pair programming worx... merge integration and weave
704
        return (revision_id is None
705
                or revision_id in self.revision_store)
1261 by Martin Pool
- new method Branch.has_revision
706
707
1182 by Martin Pool
- more disentangling of xml storage format from objects
708
    def get_revision_xml_file(self, revision_id):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
709
        """Return XML file object for revision object."""
710
        if not revision_id or not isinstance(revision_id, basestring):
711
            raise InvalidRevisionId(revision_id)
712
713
        self.lock_read()
714
        try:
715
            try:
716
                return self.revision_store[revision_id]
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
717
            except (IndexError, KeyError):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
718
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
719
        finally:
720
            self.unlock()
721
722
1231 by Martin Pool
- more progress on fetch on top of weaves
723
    def get_revision_xml(self, revision_id):
724
        return self.get_revision_xml_file(revision_id).read()
725
726
1 by mbp at sourcefrog
import from baz patch-364
727
    def get_revision(self, revision_id):
728
        """Return the Revision object for a named revision"""
1182 by Martin Pool
- more disentangling of xml storage format from objects
729
        xml_file = self.get_revision_xml_file(revision_id)
1027 by Martin Pool
- better error message when failing to get revision from store
730
731
        try:
1189 by Martin Pool
- BROKEN: partial support for commit into weave
732
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
733
        except SyntaxError, e:
734
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
735
                                         [revision_id,
736
                                          str(e)])
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
737
            
1 by mbp at sourcefrog
import from baz patch-364
738
        assert r.revision_id == revision_id
739
        return r
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
740
741
742
    def get_revision_delta(self, revno):
743
        """Return the delta for one revision.
744
745
        The delta is relative to its mainline predecessor, or the
746
        empty tree for revision 1.
747
        """
748
        assert isinstance(revno, int)
749
        rh = self.revision_history()
750
        if not (1 <= revno <= len(rh)):
751
            raise InvalidRevisionNumber(revno)
752
753
        # revno is 1-based; list is 0-based
754
755
        new_tree = self.revision_tree(rh[revno-1])
756
        if revno == 1:
757
            old_tree = EmptyTree()
758
        else:
759
            old_tree = self.revision_tree(rh[revno-2])
760
761
        return compare_trees(old_tree, new_tree)
762
672 by Martin Pool
- revision records include the hash of their inventory and
763
    def get_revision_sha1(self, revision_id):
764
        """Hash the stored value of a revision, and return it."""
1230 by Martin Pool
- remove Branch.get_revision_xml; use get_revision_xml_file instead
765
        return bzrlib.osutils.sha_file(self.get_revision_xml_file(revision_id))
672 by Martin Pool
- revision records include the hash of their inventory and
766
1 by mbp at sourcefrog
import from baz patch-364
767
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
768
    def _get_ancestry_weave(self):
769
        return self.control_weaves.get_weave('ancestry')
1390 by Robert Collins
pair programming worx... merge integration and weave
770
        
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
771
1225 by Martin Pool
- branch now tracks ancestry - all merged revisions
772
    def get_ancestry(self, revision_id):
773
        """Return a list of revision-ids integrated by a revision.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
774
        """
1225 by Martin Pool
- branch now tracks ancestry - all merged revisions
775
        # strip newlines
1390 by Robert Collins
pair programming worx... merge integration and weave
776
        if revision_id is None:
777
            return [None]
778
        w = self._get_ancestry_weave()
779
        return [None] + [l[:-1] for l in w.get_iter(w.lookup(revision_id))]
1225 by Martin Pool
- branch now tracks ancestry - all merged revisions
780
781
1223 by Martin Pool
- store inventories in weave
782
    def get_inventory_weave(self):
1352 by Martin Pool
- store control weaves in .bzr/, not mixed in with file weaves
783
        return self.control_weaves.get_weave('inventory')
1223 by Martin Pool
- store inventories in weave
784
785
1192 by Martin Pool
- clean up code for retrieving stored inventories
786
    def get_inventory(self, revision_id):
1223 by Martin Pool
- store inventories in weave
787
        """Get Inventory object by hash."""
1372 by Martin Pool
- avoid converting inventories to/from StringIO
788
        xml = self.get_inventory_xml(revision_id)
789
        return bzrlib.xml5.serializer_v5.read_inventory_from_string(xml)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
790
791
1192 by Martin Pool
- clean up code for retrieving stored inventories
792
    def get_inventory_xml(self, revision_id):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
793
        """Get inventory XML as a file object."""
1192 by Martin Pool
- clean up code for retrieving stored inventories
794
        try:
795
            assert isinstance(revision_id, basestring), type(revision_id)
1223 by Martin Pool
- store inventories in weave
796
            iw = self.get_inventory_weave()
797
            return iw.get_text(iw.lookup(revision_id))
1192 by Martin Pool
- clean up code for retrieving stored inventories
798
        except IndexError:
799
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
800
1 by mbp at sourcefrog
import from baz patch-364
801
1192 by Martin Pool
- clean up code for retrieving stored inventories
802
    def get_inventory_sha1(self, revision_id):
672 by Martin Pool
- revision records include the hash of their inventory and
803
        """Return the sha1 hash of the inventory entry
804
        """
1223 by Martin Pool
- store inventories in weave
805
        return self.get_revision(revision_id).inventory_sha1
672 by Martin Pool
- revision records include the hash of their inventory and
806
1 by mbp at sourcefrog
import from baz patch-364
807
808
    def get_revision_inventory(self, revision_id):
809
        """Return inventory of a past revision."""
1372 by Martin Pool
- avoid converting inventories to/from StringIO
810
        # TODO: Unify this with get_inventory()
1218 by Martin Pool
- fix up import
811
        # bzr 0.0.6 and later imposes the constraint that the inventory_id
820 by Martin Pool
- faster Branch.get_revision_inventory now we know the ids are the same
812
        # must be the same as its revision, so this is trivial.
1 by mbp at sourcefrog
import from baz patch-364
813
        if revision_id == None:
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
814
            return Inventory(self.get_root_id())
1 by mbp at sourcefrog
import from baz patch-364
815
        else:
820 by Martin Pool
- faster Branch.get_revision_inventory now we know the ids are the same
816
            return self.get_inventory(revision_id)
1 by mbp at sourcefrog
import from baz patch-364
817
818
819
    def revision_history(self):
1295 by Martin Pool
- remove pointless doctest
820
        """Return sequence of revision hashes on to this branch."""
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
821
        self.lock_read()
822
        try:
823
            return [l.rstrip('\r\n') for l in
824
                    self.controlfile('revision-history', 'r').readlines()]
825
        finally:
826
            self.unlock()
1 by mbp at sourcefrog
import from baz patch-364
827
828
622 by Martin Pool
Updated merge patch from Aaron
829
    def common_ancestor(self, other, self_revno=None, other_revno=None):
830
        """
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
831
        >>> from bzrlib.commit import commit
622 by Martin Pool
Updated merge patch from Aaron
832
        >>> sb = ScratchBranch(files=['foo', 'foo~'])
833
        >>> sb.common_ancestor(sb) == (None, None)
834
        True
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
835
        >>> commit(sb, "Committing first revision", verbose=False)
622 by Martin Pool
Updated merge patch from Aaron
836
        >>> sb.common_ancestor(sb)[0]
837
        1
838
        >>> clone = sb.clone()
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
839
        >>> commit(sb, "Committing second revision", verbose=False)
622 by Martin Pool
Updated merge patch from Aaron
840
        >>> sb.common_ancestor(sb)[0]
841
        2
842
        >>> sb.common_ancestor(clone)[0]
843
        1
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
844
        >>> commit(clone, "Committing divergent second revision", 
622 by Martin Pool
Updated merge patch from Aaron
845
        ...               verbose=False)
846
        >>> sb.common_ancestor(clone)[0]
847
        1
848
        >>> sb.common_ancestor(clone) == clone.common_ancestor(sb)
849
        True
850
        >>> sb.common_ancestor(sb) != clone.common_ancestor(clone)
851
        True
852
        >>> clone2 = sb.clone()
853
        >>> sb.common_ancestor(clone2)[0]
854
        2
855
        >>> sb.common_ancestor(clone2, self_revno=1)[0]
856
        1
857
        >>> sb.common_ancestor(clone2, other_revno=1)[0]
858
        1
859
        """
860
        my_history = self.revision_history()
861
        other_history = other.revision_history()
862
        if self_revno is None:
863
            self_revno = len(my_history)
864
        if other_revno is None:
865
            other_revno = len(other_history)
866
        indices = range(min((self_revno, other_revno)))
867
        indices.reverse()
868
        for r in indices:
869
            if my_history[r] == other_history[r]:
870
                return r+1, my_history[r]
871
        return None, None
872
385 by Martin Pool
- New Branch.enum_history method
873
1 by mbp at sourcefrog
import from baz patch-364
874
    def revno(self):
875
        """Return current revision number for this branch.
876
877
        That is equivalent to the number of revisions committed to
878
        this branch.
879
        """
880
        return len(self.revision_history())
881
882
1241 by Martin Pool
- rename last_patch to last_revision
883
    def last_revision(self):
1 by mbp at sourcefrog
import from baz patch-364
884
        """Return last patch hash, or None if no history.
885
        """
886
        ph = self.revision_history()
887
        if ph:
888
            return ph[-1]
184 by mbp at sourcefrog
pychecker fixups
889
        else:
890
            return None
485 by Martin Pool
- move commit code into its own module
891
892
974.1.27 by aaron.bentley at utoronto
Initial greedy fetch work
893
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
1260 by Martin Pool
- some updates for fetch/update function
894
        """Return a list of new revisions that would perfectly fit.
895
        
628 by Martin Pool
- merge aaron's updated merge/pull code
896
        If self and other have not diverged, return a list of the revisions
897
        present in other, but missing from self.
898
899
        >>> from bzrlib.commit import commit
900
        >>> bzrlib.trace.silent = True
901
        >>> br1 = ScratchBranch()
902
        >>> br2 = ScratchBranch()
903
        >>> br1.missing_revisions(br2)
904
        []
905
        >>> commit(br2, "lala!", rev_id="REVISION-ID-1")
906
        >>> br1.missing_revisions(br2)
907
        [u'REVISION-ID-1']
908
        >>> br2.missing_revisions(br1)
909
        []
910
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1")
911
        >>> br1.missing_revisions(br2)
912
        []
913
        >>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
914
        >>> br1.missing_revisions(br2)
915
        [u'REVISION-ID-2A']
916
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
917
        >>> br1.missing_revisions(br2)
918
        Traceback (most recent call last):
919
        DivergedBranches: These branches have diverged.
920
        """
1260 by Martin Pool
- some updates for fetch/update function
921
        # FIXME: If the branches have diverged, but the latest
922
        # revision in this branch is completely merged into the other,
923
        # then we should still be able to pull.
628 by Martin Pool
- merge aaron's updated merge/pull code
924
        self_history = self.revision_history()
925
        self_len = len(self_history)
926
        other_history = other.revision_history()
927
        other_len = len(other_history)
928
        common_index = min(self_len, other_len) -1
929
        if common_index >= 0 and \
930
            self_history[common_index] != other_history[common_index]:
931
            raise DivergedBranches(self, other)
685 by Martin Pool
- add -r option to the branch command
932
933
        if stop_revision is None:
934
            stop_revision = other_len
1273 by Martin Pool
- fix up copy_branch, etc
935
        else:
936
            assert isinstance(stop_revision, int)
937
            if stop_revision > other_len:
938
                raise bzrlib.errors.NoSuchRevision(self, stop_revision)
685 by Martin Pool
- add -r option to the branch command
939
        return other_history[self_len:stop_revision]
940
974.1.28 by aaron.bentley at utoronto
factored install_revisions out of update_revisions, updated test cases for greedy_fetch
941
    def update_revisions(self, other, stop_revision=None):
1390 by Robert Collins
pair programming worx... merge integration and weave
942
        """Pull in new perfect-fit revisions."""
974.1.33 by aaron.bentley at utoronto
Added greedy_fetch to update_revisions
943
        from bzrlib.fetch import greedy_fetch
974.1.74 by Aaron Bentley
Made pull work after remote branch has merged latest revision
944
        from bzrlib.revision import get_intervening_revisions
974.1.75 by Aaron Bentley
Sped up pull by copying locally first
945
        if stop_revision is None:
1390 by Robert Collins
pair programming worx... merge integration and weave
946
            stop_revision = other.last_revision()
1260 by Martin Pool
- some updates for fetch/update function
947
        greedy_fetch(to_branch=self, from_branch=other,
1261 by Martin Pool
- new method Branch.has_revision
948
                     revision=stop_revision)
1390 by Robert Collins
pair programming worx... merge integration and weave
949
        pullable_revs = self.missing_revisions(
950
            other, other.revision_id_to_revno(stop_revision))
1261 by Martin Pool
- new method Branch.has_revision
951
        if pullable_revs:
952
            greedy_fetch(to_branch=self,
953
                         from_branch=other,
954
                         revision=pullable_revs[-1])
955
            self.append_revision(*pullable_revs)
1390 by Robert Collins
pair programming worx... merge integration and weave
956
    
1110 by Martin Pool
- merge aaron's merge improvements:
957
485 by Martin Pool
- move commit code into its own module
958
    def commit(self, *args, **kw):
1189 by Martin Pool
- BROKEN: partial support for commit into weave
959
        from bzrlib.commit import Commit
960
        Commit().commit(self, *args, **kw)
1390 by Robert Collins
pair programming worx... merge integration and weave
961
    
1105 by Martin Pool
- expose 'find-merge-base' as a new expert command,
962
    def revision_id_to_revno(self, revision_id):
963
        """Given a revision id, return its revno"""
1390 by Robert Collins
pair programming worx... merge integration and weave
964
        if revision_id is None:
965
            return 0
1105 by Martin Pool
- expose 'find-merge-base' as a new expert command,
966
        history = self.revision_history()
967
        try:
968
            return history.index(revision_id) + 1
969
        except ValueError:
970
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
971
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
972
    def get_rev_id(self, revno, history=None):
973
        """Find the revision id of the specified revno."""
974
        if revno == 0:
975
            return None
976
        if history is None:
977
            history = self.revision_history()
978
        elif revno <= 0 or revno > len(history):
979
            raise bzrlib.errors.NoSuchRevision(self, revno)
980
        return history[revno - 1]
981
1 by mbp at sourcefrog
import from baz patch-364
982
    def revision_tree(self, revision_id):
983
        """Return Tree for a revision on this branch.
984
985
        `revision_id` may be None for the null revision, in which case
986
        an `EmptyTree` is returned."""
529 by Martin Pool
todo
987
        # TODO: refactor this to use an existing revision object
988
        # so we don't need to read it in twice.
1 by mbp at sourcefrog
import from baz patch-364
989
        if revision_id == None:
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
990
            return EmptyTree()
1 by mbp at sourcefrog
import from baz patch-364
991
        else:
992
            inv = self.get_revision_inventory(revision_id)
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
993
            return RevisionTree(self.weave_store, inv, revision_id)
1 by mbp at sourcefrog
import from baz patch-364
994
995
996
    def working_tree(self):
997
        """Return a `Tree` for the working copy."""
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
998
        from bzrlib.workingtree import WorkingTree
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
999
        # TODO: In the future, WorkingTree should utilize Transport
1000
        return WorkingTree(self._transport.base, self.read_working_inventory())
1 by mbp at sourcefrog
import from baz patch-364
1001
1002
1003
    def basis_tree(self):
1004
        """Return `Tree` object for last revision.
1005
1006
        If there are no revisions yet, return an `EmptyTree`.
1007
        """
1241 by Martin Pool
- rename last_patch to last_revision
1008
        return self.revision_tree(self.last_revision())
1 by mbp at sourcefrog
import from baz patch-364
1009
1010
168 by mbp at sourcefrog
new "rename" command
1011
    def rename_one(self, from_rel, to_rel):
309 by Martin Pool
doc
1012
        """Rename one file.
1013
1014
        This can change the directory or the filename or both.
353 by Martin Pool
- Per-branch locks in read and write modes.
1015
        """
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1016
        self.lock_write()
171 by mbp at sourcefrog
better error message when working file rename fails
1017
        try:
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1018
            tree = self.working_tree()
1019
            inv = tree.inventory
1020
            if not tree.has_filename(from_rel):
1021
                raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1022
            if tree.has_filename(to_rel):
1023
                raise BzrError("can't rename: new working file %r already exists" % to_rel)
1024
1025
            file_id = inv.path2id(from_rel)
1026
            if file_id == None:
1027
                raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1028
1029
            if inv.path2id(to_rel):
1030
                raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1031
1032
            to_dir, to_tail = os.path.split(to_rel)
1033
            to_dir_id = inv.path2id(to_dir)
1034
            if to_dir_id == None and to_dir != '':
1035
                raise BzrError("can't determine destination directory id for %r" % to_dir)
1036
1037
            mutter("rename_one:")
1038
            mutter("  file_id    {%s}" % file_id)
1039
            mutter("  from_rel   %r" % from_rel)
1040
            mutter("  to_rel     %r" % to_rel)
1041
            mutter("  to_dir     %r" % to_dir)
1042
            mutter("  to_dir_id  {%s}" % to_dir_id)
1043
1044
            inv.rename(file_id, to_dir_id, to_tail)
1045
1046
            from_abs = self.abspath(from_rel)
1047
            to_abs = self.abspath(to_rel)
1048
            try:
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
1049
                rename(from_abs, to_abs)
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1050
            except OSError, e:
1051
                raise BzrError("failed to rename %r to %r: %s"
1052
                        % (from_abs, to_abs, e[1]),
1053
                        ["rename rolled back"])
1054
1055
            self._write_inventory(inv)
1056
        finally:
1057
            self.unlock()
1058
1059
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
1060
    def move(self, from_paths, to_name):
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1061
        """Rename files.
1062
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
1063
        to_name must exist as a versioned directory.
1064
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1065
        If to_name exists and is a directory, the files are moved into
1066
        it, keeping their old names.  If it is a directory, 
1067
1068
        Note that to_name is only the last component of the new name;
1069
        this doesn't change the directory.
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1070
1071
        This returns a list of (from_path, to_path) pairs for each
1072
        entry that is moved.
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1073
        """
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1074
        result = []
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1075
        self.lock_write()
1076
        try:
1077
            ## TODO: Option to move IDs only
1078
            assert not isinstance(from_paths, basestring)
1079
            tree = self.working_tree()
1080
            inv = tree.inventory
1081
            to_abs = self.abspath(to_name)
1082
            if not isdir(to_abs):
1083
                raise BzrError("destination %r is not a directory" % to_abs)
1084
            if not tree.has_filename(to_name):
1085
                raise BzrError("destination %r not in working directory" % to_abs)
1086
            to_dir_id = inv.path2id(to_name)
1087
            if to_dir_id == None and to_name != '':
1088
                raise BzrError("destination %r is not a versioned directory" % to_name)
1089
            to_dir_ie = inv[to_dir_id]
1090
            if to_dir_ie.kind not in ('directory', 'root_directory'):
1091
                raise BzrError("destination %r is not a directory" % to_abs)
1092
1093
            to_idpath = inv.get_idpath(to_dir_id)
1094
1095
            for f in from_paths:
1096
                if not tree.has_filename(f):
1097
                    raise BzrError("%r does not exist in working tree" % f)
1098
                f_id = inv.path2id(f)
1099
                if f_id == None:
1100
                    raise BzrError("%r is not versioned" % f)
1101
                name_tail = splitpath(f)[-1]
1102
                dest_path = appendpath(to_name, name_tail)
1103
                if tree.has_filename(dest_path):
1104
                    raise BzrError("destination %r already exists" % dest_path)
1105
                if f_id in to_idpath:
1106
                    raise BzrError("can't move %r to a subdirectory of itself" % f)
1107
1108
            # OK, so there's a race here, it's possible that someone will
1109
            # create a file in this interval and then the rename might be
1110
            # left half-done.  But we should have caught most problems.
1111
1112
            for f in from_paths:
1113
                name_tail = splitpath(f)[-1]
1114
                dest_path = appendpath(to_name, name_tail)
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1115
                result.append((f, dest_path))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1116
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1117
                try:
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
1118
                    rename(self.abspath(f), self.abspath(dest_path))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1119
                except OSError, e:
1120
                    raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1121
                            ["rename rolled back"])
1122
1123
            self._write_inventory(inv)
1124
        finally:
1125
            self.unlock()
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1126
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1127
        return result
1128
160 by mbp at sourcefrog
- basic support for moving files to different directories - have not done support for renaming them yet, but should be straightforward - some tests, but many cases are not handled yet i think
1129
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1130
    def revert(self, filenames, old_tree=None, backups=True):
778 by Martin Pool
- simple revert of text files
1131
        """Restore selected files to the versions from a previous tree.
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1132
1133
        backups
1134
            If true (default) backups are made of files before
1135
            they're renamed.
778 by Martin Pool
- simple revert of text files
1136
        """
1137
        from bzrlib.errors import NotVersionedError, BzrError
1138
        from bzrlib.atomicfile import AtomicFile
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1139
        from bzrlib.osutils import backup_file
778 by Martin Pool
- simple revert of text files
1140
        
1141
        inv = self.read_working_inventory()
1142
        if old_tree is None:
1143
            old_tree = self.basis_tree()
1144
        old_inv = old_tree.inventory
1145
1146
        nids = []
1147
        for fn in filenames:
1148
            file_id = inv.path2id(fn)
1149
            if not file_id:
1150
                raise NotVersionedError("not a versioned file", fn)
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1151
            if not old_inv.has_id(file_id):
1152
                raise BzrError("file not present in old tree", fn, file_id)
778 by Martin Pool
- simple revert of text files
1153
            nids.append((fn, file_id))
1154
            
1155
        # TODO: Rename back if it was previously at a different location
1156
1157
        # TODO: If given a directory, restore the entire contents from
1158
        # the previous version.
1159
1160
        # TODO: Make a backup to a temporary file.
1161
1162
        # TODO: If the file previously didn't exist, delete it?
1163
        for fn, file_id in nids:
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1164
            backup_file(fn)
1165
            
778 by Martin Pool
- simple revert of text files
1166
            f = AtomicFile(fn, 'wb')
1167
            try:
1168
                f.write(old_tree.get_file(file_id).read())
1169
                f.commit()
1170
            finally:
1171
                f.close()
1172
1173
815 by Martin Pool
- track pending-merges
1174
    def pending_merges(self):
1175
        """Return a list of pending merges.
1176
1177
        These are revisions that have been merged into the working
1178
        directory but not yet committed.
1179
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
1180
        cfn = self._rel_controlfilename('pending-merges')
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
1181
        if not self._transport.has(cfn):
815 by Martin Pool
- track pending-merges
1182
            return []
1183
        p = []
1184
        for l in self.controlfile('pending-merges', 'r').readlines():
1185
            p.append(l.rstrip('\n'))
1186
        return p
1187
1188
907.1.4 by John Arbash Meinel
Add pending_merge can take multiple entries.
1189
    def add_pending_merge(self, *revision_ids):
1263 by Martin Pool
- clean up imports
1190
        # TODO: Perhaps should check at this point that the
1191
        # history of the revision is actually present?
907.1.4 by John Arbash Meinel
Add pending_merge can take multiple entries.
1192
        for rev_id in revision_ids:
1193
            validate_revision_id(rev_id)
815 by Martin Pool
- track pending-merges
1194
1195
        p = self.pending_merges()
907.1.4 by John Arbash Meinel
Add pending_merge can take multiple entries.
1196
        updated = False
1197
        for rev_id in revision_ids:
1198
            if rev_id in p:
1199
                continue
1200
            p.append(rev_id)
1201
            updated = True
1202
        if updated:
1203
            self.set_pending_merges(p)
815 by Martin Pool
- track pending-merges
1204
1205
    def set_pending_merges(self, rev_list):
1206
        self.lock_write()
1207
        try:
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
1208
            self.put_controlfile('pending-merges', '\n'.join(rev_list))
815 by Martin Pool
- track pending-merges
1209
        finally:
1210
            self.unlock()
1211
1212
1149 by Martin Pool
- make get_parent() be a method of Branch; add simple tests for it
1213
    def get_parent(self):
1214
        """Return the parent location of the branch.
1215
1216
        This is the default location for push/pull/missing.  The usual
1217
        pattern is that the user can override it by specifying a
1218
        location.
1219
        """
1220
        import errno
1221
        _locs = ['parent', 'pull', 'x-pull']
1222
        for l in _locs:
1223
            try:
1224
                return self.controlfile(l, 'r').read().strip('\n')
1225
            except IOError, e:
1226
                if e.errno != errno.ENOENT:
1227
                    raise
1228
        return None
1229
1150 by Martin Pool
- add new Branch.set_parent and tests
1230
1231
    def set_parent(self, url):
1232
        # TODO: Maybe delete old location files?
1233
        from bzrlib.atomicfile import AtomicFile
1234
        self.lock_write()
1235
        try:
1236
            f = AtomicFile(self.controlfilename('parent'))
1237
            try:
1238
                f.write(url + '\n')
1239
                f.commit()
1240
            finally:
1241
                f.close()
1242
        finally:
1243
            self.unlock()
1244
974.1.54 by aaron.bentley at utoronto
Fixed the revno bug in log
1245
    def check_revno(self, revno):
1246
        """\
1247
        Check whether a revno corresponds to any revision.
1248
        Zero (the NULL revision) is considered valid.
1249
        """
1250
        if revno != 0:
1251
            self.check_real_revno(revno)
1252
            
1253
    def check_real_revno(self, revno):
1254
        """\
1255
        Check whether a revno corresponds to a real revision.
1256
        Zero (the NULL revision) is considered invalid
1257
        """
1258
        if revno < 1 or revno > self.revno():
1259
            raise InvalidRevisionNumber(revno)
1260
        
1149 by Martin Pool
- make get_parent() be a method of Branch; add simple tests for it
1261
        
1185.11.5 by John Arbash Meinel
Merged up-to-date against mainline, still broken.
1262
        
1263
1264
1185.11.11 by John Arbash Meinel
Removing the remotebranch object.
1265
class ScratchBranch(_Branch):
1 by mbp at sourcefrog
import from baz patch-364
1266
    """Special test class: a branch that cleans up after itself.
1267
1268
    >>> b = ScratchBranch()
1269
    >>> isdir(b.base)
1270
    True
1271
    >>> bd = b.base
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1272
    >>> b.destroy()
1 by mbp at sourcefrog
import from baz patch-364
1273
    >>> isdir(bd)
1274
    False
1275
    """
622 by Martin Pool
Updated merge patch from Aaron
1276
    def __init__(self, files=[], dirs=[], base=None):
1 by mbp at sourcefrog
import from baz patch-364
1277
        """Make a test branch.
1278
1279
        This creates a temporary directory and runs init-tree in it.
1280
1281
        If any files are listed, they are created in the working copy.
1282
        """
800 by Martin Pool
Merge John's import-speedup branch:
1283
        from tempfile import mkdtemp
622 by Martin Pool
Updated merge patch from Aaron
1284
        init = False
1285
        if base is None:
800 by Martin Pool
Merge John's import-speedup branch:
1286
            base = mkdtemp()
622 by Martin Pool
Updated merge patch from Aaron
1287
            init = True
1393.2.4 by John Arbash Meinel
All tests pass.
1288
        if isinstance(base, basestring):
1289
            base = get_transport(base)
1185.11.11 by John Arbash Meinel
Removing the remotebranch object.
1290
        _Branch.__init__(self, base, init=init)
100 by mbp at sourcefrog
- add test case for ignore files
1291
        for d in dirs:
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
1292
            self._transport.mkdir(d)
100 by mbp at sourcefrog
- add test case for ignore files
1293
            
1 by mbp at sourcefrog
import from baz patch-364
1294
        for f in files:
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
1295
            self._transport.put(f, 'content of %s' % f)
1 by mbp at sourcefrog
import from baz patch-364
1296
1297
622 by Martin Pool
Updated merge patch from Aaron
1298
    def clone(self):
1299
        """
1300
        >>> orig = ScratchBranch(files=["file1", "file2"])
1301
        >>> clone = orig.clone()
1185.1.40 by Robert Collins
Merge what applied of Alexander Belchenko's win32 patch.
1302
        >>> if os.name != 'nt':
1303
        ...   os.path.samefile(orig.base, clone.base)
1304
        ... else:
1305
        ...   orig.base == clone.base
1306
        ...
622 by Martin Pool
Updated merge patch from Aaron
1307
        False
1308
        >>> os.path.isfile(os.path.join(clone.base, "file1"))
1309
        True
1310
        """
800 by Martin Pool
Merge John's import-speedup branch:
1311
        from shutil import copytree
1312
        from tempfile import mkdtemp
1313
        base = mkdtemp()
622 by Martin Pool
Updated merge patch from Aaron
1314
        os.rmdir(base)
800 by Martin Pool
Merge John's import-speedup branch:
1315
        copytree(self.base, base, symlinks=True)
622 by Martin Pool
Updated merge patch from Aaron
1316
        return ScratchBranch(base=base)
1149 by Martin Pool
- make get_parent() be a method of Branch; add simple tests for it
1317
1318
622 by Martin Pool
Updated merge patch from Aaron
1319
        
1 by mbp at sourcefrog
import from baz patch-364
1320
    def __del__(self):
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1321
        self.destroy()
1322
1323
    def destroy(self):
1 by mbp at sourcefrog
import from baz patch-364
1324
        """Destroy the test branch, removing the scratch directory."""
800 by Martin Pool
Merge John's import-speedup branch:
1325
        from shutil import rmtree
163 by mbp at sourcefrog
merge win32 portability fixes
1326
        try:
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
1327
            if self.base:
1328
                mutter("delete ScratchBranch %s" % self.base)
800 by Martin Pool
Merge John's import-speedup branch:
1329
                rmtree(self.base)
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1330
        except OSError, e:
163 by mbp at sourcefrog
merge win32 portability fixes
1331
            # Work around for shutil.rmtree failing on Windows when
1332
            # readonly files are encountered
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1333
            mutter("hit exception in destroying ScratchBranch: %s" % e)
163 by mbp at sourcefrog
merge win32 portability fixes
1334
            for root, dirs, files in os.walk(self.base, topdown=False):
1335
                for name in files:
1336
                    os.chmod(os.path.join(root, name), 0700)
800 by Martin Pool
Merge John's import-speedup branch:
1337
            rmtree(self.base)
907.1.19 by John Arbash Meinel
Updated ScratchBranch and Branch.base, All Tests PASS !!!
1338
        self._transport = None
1 by mbp at sourcefrog
import from baz patch-364
1339
1340
    
1341
1342
######################################################################
1343
# predicates
1344
1345
1346
def is_control_file(filename):
1347
    ## FIXME: better check
1348
    filename = os.path.normpath(filename)
1349
    while filename != '':
1350
        head, tail = os.path.split(filename)
1351
        ## mutter('check %r for control file' % ((head, tail), ))
1352
        if tail == bzrlib.BZRDIR:
1353
            return True
70 by mbp at sourcefrog
Prepare for smart recursive add.
1354
        if filename == head:
1355
            break
1 by mbp at sourcefrog
import from baz patch-364
1356
        filename = head
1357
    return False
1358
1359
1360
70 by mbp at sourcefrog
Prepare for smart recursive add.
1361
def gen_file_id(name):
1 by mbp at sourcefrog
import from baz patch-364
1362
    """Return new file id.
1363
1364
    This should probably generate proper UUIDs, but for the moment we
1365
    cope with just randomness because running uuidgen every time is
1366
    slow."""
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1367
    import re
800 by Martin Pool
Merge John's import-speedup branch:
1368
    from binascii import hexlify
1369
    from time import time
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1370
1371
    # get last component
70 by mbp at sourcefrog
Prepare for smart recursive add.
1372
    idx = name.rfind('/')
1373
    if idx != -1:
1374
        name = name[idx+1 : ]
262 by Martin Pool
- gen_file_id: break the file on either / or \ when looking
1375
    idx = name.rfind('\\')
1376
    if idx != -1:
1377
        name = name[idx+1 : ]
70 by mbp at sourcefrog
Prepare for smart recursive add.
1378
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1379
    # make it not a hidden file
70 by mbp at sourcefrog
Prepare for smart recursive add.
1380
    name = name.lstrip('.')
1381
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1382
    # remove any wierd characters; we don't escape them but rather
1383
    # just pull them out
1384
    name = re.sub(r'[^\w.]', '', name)
1385
190 by mbp at sourcefrog
64 bits of randomness in file/revision ids
1386
    s = hexlify(rand_bytes(8))
800 by Martin Pool
Merge John's import-speedup branch:
1387
    return '-'.join((name, compact_date(time()), s))
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
1388
1389
1390
def gen_root_id():
1391
    """Return a new tree-root file id."""
1392
    return gen_file_id('TREE_ROOT')
1393
1092.1.34 by Robert Collins
unbreak cmd_branch now that something tests the core of it..
1394