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