/brz/remove-bazaar

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