~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
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):
907.1.45 by John Arbash Meinel
Switch to registering protocol handlers, rather than just updating a dictionary.
51
    from bzrlib.transport import transport
52
    from bzrlib.local_transport import LocalTransport
907.1.10 by John Arbash Meinel
Updated find_branch so that it can search for the branch root.
53
    t = transport(f)
54
    # FIXME: This is a hack around transport so that
55
    #        We can search the local directories for
56
    #        a branch root.
907.1.13 by John Arbash Meinel
Fixed bzr root.
57
    if args.has_key('init') and args['init']:
58
        # Don't search if we are init-ing
59
        return Branch(t, **args)
907.1.10 by John Arbash Meinel
Updated find_branch so that it can search for the branch root.
60
    if isinstance(t, LocalTransport):
61
        root = find_branch_root(f)
62
        if root != f:
63
            t = transport(root)
64
    return Branch(t, **args)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
65
600 by Martin Pool
- Better Branch.relpath that doesn't match on
66
def _relpath(base, path):
67
    """Return path relative to base, or raise exception.
68
69
    The path may be either an absolute path or a path relative to the
70
    current working directory.
71
72
    Lifted out of Branch.relpath for ease of testing.
73
74
    os.path.commonprefix (python2.4) has a bad bug that it works just
75
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
76
    avoids that problem."""
77
    rp = os.path.abspath(path)
78
79
    s = []
80
    head = rp
81
    while len(head) >= len(base):
82
        if head == base:
83
            break
84
        head, tail = os.path.split(head)
85
        if tail:
86
            s.insert(0, tail)
87
    else:
88
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
89
90
    return os.sep.join(s)
416 by Martin Pool
- bzr log and bzr root now accept an http URL
91
        
92
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
93
def find_branch_root(f=None):
94
    """Find the branch root enclosing f, or pwd.
95
416 by Martin Pool
- bzr log and bzr root now accept an http URL
96
    f may be a filename or a URL.
97
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
98
    It is not necessary that f exists.
99
100
    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
101
    run into the root.  If there isn't one, raises NotBranchError.
102
    """
184 by mbp at sourcefrog
pychecker fixups
103
    if f == None:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
104
        f = os.getcwd()
907.1.10 by John Arbash Meinel
Updated find_branch so that it can search for the branch root.
105
    else:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
106
        f = os.path.realpath(f)
425 by Martin Pool
- check from aaron for existence of a branch
107
    if not os.path.exists(f):
108
        raise BzrError('%r does not exist' % f)
109
        
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
110
111
    orig_f = f
112
113
    while True:
114
        if os.path.exists(os.path.join(f, bzrlib.BZRDIR)):
115
            return f
116
        head, tail = os.path.split(f)
117
        if head == f:
118
            # reached the root, whatever that may be
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
119
            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
120
        f = head
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
121
122
123
685 by Martin Pool
- add -r option to the branch command
124
1 by mbp at sourcefrog
import from baz patch-364
125
######################################################################
126
# branch objects
127
558 by Martin Pool
- All top-level classes inherit from object
128
class Branch(object):
1 by mbp at sourcefrog
import from baz patch-364
129
    """Branch holding a history of revisions.
130
343 by Martin Pool
doc
131
    base
132
        Base directory of the branch.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
133
134
    _lock_mode
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
135
        None, or 'r' or 'w'
136
137
    _lock_count
138
        If _lock_mode is true, a positive count of the number of times the
139
        lock has been taken.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
140
614 by Martin Pool
- unify two defintions of LockError
141
    _lock
142
        Lock object from bzrlib.lock.
1 by mbp at sourcefrog
import from baz patch-364
143
    """
564 by Martin Pool
- Set Branch.base in class def to avoid it being undefined
144
    base = None
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
145
    _lock_mode = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
146
    _lock_count = None
615 by Martin Pool
Major rework of locking code:
147
    _lock = None
907.1.24 by John Arbash Meinel
Remote functionality work.
148
    cache_root = None
353 by Martin Pool
- Per-branch locks in read and write modes.
149
    
897 by Martin Pool
- merge john's revision-naming code
150
    # Map some sort of prefix into a namespace
151
    # stuff like "revno:10", "revid:", etc.
152
    # This should match a prefix with a function which accepts
153
    REVISION_NAMESPACES = {}
154
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
155
    def __init__(self, transport, init=False):
1 by mbp at sourcefrog
import from baz patch-364
156
        """Create new branch object at a particular location.
157
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
158
        transport -- A Transport object, defining how to access files.
159
                (If a string, transport.transport() will be used to
160
                create a Transport object)
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
161
        
254 by Martin Pool
- Doc cleanups from Magnus Therning
162
        init -- If True, create new control files in a previously
1 by mbp at sourcefrog
import from baz patch-364
163
             unversioned directory.  If False, the branch must already
164
             be versioned.
165
166
        In the test suite, creation of new trees is tested using the
167
        `ScratchBranch` class.
168
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
169
        if isinstance(transport, basestring):
170
            from transport import transport as get_transport
171
            transport = get_transport(transport)
172
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
173
        self._transport = transport
1 by mbp at sourcefrog
import from baz patch-364
174
        if init:
175
            self._make_control()
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
176
        self._check_format()
1 by mbp at sourcefrog
import from baz patch-364
177
178
179
    def __str__(self):
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
180
        return '%s(%r)' % (self.__class__.__name__, self._transport.base)
1 by mbp at sourcefrog
import from baz patch-364
181
182
183
    __repr__ = __str__
184
185
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
186
    def __del__(self):
615 by Martin Pool
Major rework of locking code:
187
        if self._lock_mode or self._lock:
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
188
            from bzrlib.warnings import warn
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
189
            warn("branch %r was not explicitly unlocked" % self)
615 by Martin Pool
Major rework of locking code:
190
            self._lock.unlock()
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
191
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
192
        # TODO: It might be best to do this somewhere else,
193
        # but it is nice for a Branch object to automatically
194
        # cache it's information.
195
        # Alternatively, we could have the Transport objects cache requests
196
        # See the earlier discussion about how major objects (like Branch)
197
        # should never expect their __del__ function to run.
198
        if self.cache_root is not None:
907.1.24 by John Arbash Meinel
Remote functionality work.
199
            #from warnings import warn
200
            #warn("branch %r auto-cleanup of cache files" % self)
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
201
            try:
202
                import shutil
203
                shutil.rmtree(self.cache_root)
204
            except:
205
                pass
206
            self.cache_root = None
207
907.1.17 by John Arbash Meinel
Adding a Branch.base property, removing pull_loc()
208
    def _get_base(self):
907.1.19 by John Arbash Meinel
Updated ScratchBranch and Branch.base, All Tests PASS !!!
209
        if self._transport:
210
            return self._transport.base
211
        return None
907.1.17 by John Arbash Meinel
Adding a Branch.base property, removing pull_loc()
212
213
    base = property(_get_base)
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
214
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
215
216
    def lock_write(self):
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
217
        # TODO: Upgrade locking to support using a Transport,
218
        # and potentially a remote locking protocol
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
219
        if self._lock_mode:
220
            if self._lock_mode != 'w':
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
221
                from bzrlib.errors import LockError
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
222
                raise LockError("can't upgrade to a write lock from %r" %
223
                                self._lock_mode)
224
            self._lock_count += 1
225
        else:
907.1.24 by John Arbash Meinel
Remote functionality work.
226
            self._lock = self._transport.lock_write(
227
                    self._rel_controlfilename('branch-lock'))
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
228
            self._lock_mode = 'w'
229
            self._lock_count = 1
230
231
232
    def lock_read(self):
233
        if self._lock_mode:
234
            assert self._lock_mode in ('r', 'w'), \
235
                   "invalid lock mode %r" % self._lock_mode
236
            self._lock_count += 1
237
        else:
907.1.24 by John Arbash Meinel
Remote functionality work.
238
            self._lock = self._transport.lock_read(
239
                    self._rel_controlfilename('branch-lock'))
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
240
            self._lock_mode = 'r'
241
            self._lock_count = 1
242
                        
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
243
    def unlock(self):
244
        if not self._lock_mode:
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
245
            from bzrlib.errors import LockError
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
246
            raise LockError('branch %r is not locked' % (self))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
247
248
        if self._lock_count > 1:
249
            self._lock_count -= 1
250
        else:
615 by Martin Pool
Major rework of locking code:
251
            self._lock.unlock()
252
            self._lock = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
253
            self._lock_mode = self._lock_count = None
353 by Martin Pool
- Per-branch locks in read and write modes.
254
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
255
    def abspath(self, name):
256
        """Return absolute filename for something in the branch"""
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
257
        return self._transport.abspath(name)
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
258
68 by mbp at sourcefrog
- new relpath command and function
259
    def relpath(self, path):
260
        """Return path relative to this branch of something inside it.
261
262
        Raises an error if path is not in this branch."""
907.1.24 by John Arbash Meinel
Remote functionality work.
263
        return self._transport.relpath(path)
68 by mbp at sourcefrog
- new relpath command and function
264
265
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
266
    def _rel_controlfilename(self, file_or_path):
267
        if isinstance(file_or_path, basestring):
268
            file_or_path = [file_or_path]
269
        return [bzrlib.BZRDIR] + file_or_path
270
1 by mbp at sourcefrog
import from baz patch-364
271
    def controlfilename(self, file_or_path):
272
        """Return location relative to branch."""
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
273
        return self._transport.abspath(self._rel_controlfilename(file_or_path))
1 by mbp at sourcefrog
import from baz patch-364
274
275
276
    def controlfile(self, file_or_path, mode='r'):
245 by mbp at sourcefrog
- control files always in utf-8-unix format
277
        """Open a control file for this branch.
278
279
        There are two classes of file in the control directory: text
280
        and binary.  binary files are untranslated byte streams.  Text
281
        control files are stored with Unix newlines and in UTF-8, even
282
        if the platform or locale defaults are different.
430 by Martin Pool
doc
283
284
        Controlfiles should almost never be opened in write mode but
285
        rather should be atomically copied and replaced using atomicfile.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
286
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
287
        import codecs
288
289
        relpath = self._rel_controlfilename(file_or_path)
290
        #TODO: codecs.open() buffers linewise, so it was overloaded with
291
        # a much larger buffer, do we need to do the same for getreader/getwriter?
292
        if mode == 'rb': 
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
293
            return self._transport.get(relpath)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
294
        elif mode == 'wb':
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
295
            raise BzrError("Branch.controlfile(mode='wb') is not supported, use put_controlfiles")
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
296
        elif mode == 'r':
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
297
            return codecs.getreader('utf-8')(self._transport.get(relpath), errors='replace')
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
298
        elif mode == 'w':
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
299
            raise BzrError("Branch.controlfile(mode='w') is not supported, use put_controlfiles")
245 by mbp at sourcefrog
- control files always in utf-8-unix format
300
        else:
301
            raise BzrError("invalid controlfile mode %r" % mode)
302
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
303
    def put_controlfile(self, path, f, encode=True):
907.1.20 by John Arbash Meinel
Removed Transport.open(), making get + put encode/decode to utf-8
304
        """Write an entry as a controlfile.
305
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
306
        :param path: The path to put the file, relative to the .bzr control
307
                     directory
308
        :param f: A file-like or string object whose contents should be copied.
309
        :param encode:  If true, encode the contents as utf-8
310
        """
311
        self.put_controlfiles([(path, f)], encode=encode)
312
313
    def put_controlfiles(self, files, encode=True):
314
        """Write several entries as controlfiles.
315
316
        :param files: A list of [(path, file)] pairs, where the path is the directory
317
                      underneath the bzr control directory
318
        :param encode:  If true, encode the contents as utf-8
319
        """
320
        import codecs
321
        ctrl_files = []
322
        for path, f in files:
323
            if encode:
324
                if isinstance(f, basestring):
325
                    f = f.encode('utf-8', 'replace')
326
                else:
327
                    f = codecs.getwriter('utf-8')(f, errors='replace')
328
            path = self._rel_controlfilename(path)
329
            ctrl_files.append((path, f))
330
        self._transport.put_multi(ctrl_files)
1 by mbp at sourcefrog
import from baz patch-364
331
332
    def _make_control(self):
800 by Martin Pool
Merge John's import-speedup branch:
333
        from bzrlib.inventory import Inventory
907.1.20 by John Arbash Meinel
Removed Transport.open(), making get + put encode/decode to utf-8
334
        from cStringIO import StringIO
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
335
        
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
336
        # Create an empty inventory
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
337
        sio = StringIO()
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
338
        # if we want per-tree root ids then this is the place to set
339
        # them; they're not needed for now and so ommitted for
340
        # simplicity.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
341
        bzrlib.xml.serializer_v4.write_inventory(Inventory(), sio)
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
342
343
        dirs = [[], 'text-store', 'inventory-store', 'revision-store']
344
        files = [('README', 
1 by mbp at sourcefrog
import from baz patch-364
345
            "This is a Bazaar-NG control directory.\n"
907.1.47 by John Arbash Meinel
Created mkdir_multi, modified branch to use _multi forms when initializing a branch, creating a full test routine for transports
346
            "Do not change any files in this directory.\n"),
347
            ('branch-format', BZR_BRANCH_FORMAT),
348
            ('revision-history', ''),
349
            ('merged-patches', ''),
350
            ('pending-merged-patches', ''),
351
            ('branch-name', ''),
352
            ('branch-lock', ''),
353
            ('pending-merges', ''),
354
            ('inventory', sio.getvalue())
355
        ]
356
        self._transport.mkdir_multi([self._rel_controlfilename(d) for d in dirs])
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
357
        self.put_controlfiles(files)
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
358
        mutter('created control directory in ' + self._transport.base)
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
359
1 by mbp at sourcefrog
import from baz patch-364
360
    def _check_format(self):
361
        """Check this branch format is supported.
362
363
        The current tool only supports the current unstable format.
364
365
        In the future, we might need different in-memory Branch
366
        classes to support downlevel branches.  But not yet.
163 by mbp at sourcefrog
merge win32 portability fixes
367
        """
368
        # This ignores newlines so that we can open branches created
369
        # on Windows from Linux and so on.  I think it might be better
370
        # to always make all internal files in unix format.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
371
        fmt = self.controlfile('branch-format', 'r').read()
1185.1.2 by Martin Pool
- merge various windows and other fixes from Ollie Rutherfurd
372
        fmt = fmt.replace('\r\n', '\n')
1 by mbp at sourcefrog
import from baz patch-364
373
        if fmt != BZR_BRANCH_FORMAT:
576 by Martin Pool
- raise exceptions rather than using bailout()
374
            raise BzrError('sorry, branch format %r not supported' % fmt,
375
                           ['use a different bzr version',
376
                            'or remove the .bzr directory and "bzr init" again'])
1 by mbp at sourcefrog
import from baz patch-364
377
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
378
        # We know that the format is the currently supported one.
379
        # So create the rest of the entries.
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
380
        from bzrlib.store.compressed_text import CompressedTextStore
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
381
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
382
        if self._transport.should_cache():
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
383
            import tempfile
907.1.26 by John Arbash Meinel
Fixing some store stuff so that 'bzr branch' works.
384
            self.cache_root = tempfile.mkdtemp(prefix='bzr-cache')
907.1.24 by John Arbash Meinel
Remote functionality work.
385
            mutter('Branch %r using caching in %r' % (self, self.cache_root))
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
386
        else:
387
            self.cache_root = None
388
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
389
        def get_store(name):
390
            relpath = self._rel_controlfilename(name)
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
391
            store = CompressedTextStore(self._transport.clone(relpath))
907.1.32 by John Arbash Meinel
Renaming is_remote to should_cache as it is more appropriate.
392
            if self._transport.should_cache():
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
393
                from meta_store import CachedStore
907.1.24 by John Arbash Meinel
Remote functionality work.
394
                cache_path = os.path.join(self.cache_root, name)
907.1.23 by John Arbash Meinel
Branch objects now automatically create Cached stores if the protocol is_remote.
395
                os.mkdir(cache_path)
396
                store = CachedStore(store, cache_path)
397
            return store
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
398
399
        self.text_store = get_store('text-store')
400
        self.revision_store = get_store('revision-store')
401
        self.inventory_store = get_store('inventory-store')
402
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
403
    def get_root_id(self):
404
        """Return the id of this branches root"""
405
        inv = self.read_working_inventory()
406
        return inv.root.file_id
1 by mbp at sourcefrog
import from baz patch-364
407
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
408
    def set_root_id(self, file_id):
409
        inv = self.read_working_inventory()
410
        orig_root_id = inv.root.file_id
411
        del inv._byid[inv.root.file_id]
412
        inv.root.file_id = file_id
413
        inv._byid[inv.root.file_id] = inv.root
414
        for fid in inv:
415
            entry = inv[fid]
416
            if entry.parent_id in (None, orig_root_id):
417
                entry.parent_id = inv.root.file_id
418
        self._write_inventory(inv)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
419
1 by mbp at sourcefrog
import from baz patch-364
420
    def read_working_inventory(self):
421
        """Read the working inventory."""
800 by Martin Pool
Merge John's import-speedup branch:
422
        from bzrlib.inventory import Inventory
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
423
        self.lock_read()
424
        try:
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
425
            # ElementTree does its own conversion from UTF-8, so open in
426
            # binary.
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
427
            f = self.controlfile('inventory', 'rb')
1182 by Martin Pool
- more disentangling of xml storage format from objects
428
            return bzrlib.xml.serializer_v4.read_inventory(f)
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
429
        finally:
430
            self.unlock()
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
431
            
1 by mbp at sourcefrog
import from baz patch-364
432
433
    def _write_inventory(self, inv):
434
        """Update the working inventory.
435
436
        That is to say, the inventory describing changes underway, that
437
        will be committed to the next revision.
438
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
439
        from cStringIO import StringIO
770 by Martin Pool
- write new working inventory using AtomicFile
440
        self.lock_write()
441
        try:
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
442
            sio = StringIO()
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
443
            bzrlib.xml.serializer_v4.write_inventory(inv, sio)
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
444
            sio.seek(0)
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
445
            # Transport handles atomicity
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
446
            self.put_controlfile('inventory', sio)
770 by Martin Pool
- write new working inventory using AtomicFile
447
        finally:
448
            self.unlock()
449
        
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
450
        mutter('wrote working inventory')
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
451
            
1 by mbp at sourcefrog
import from baz patch-364
452
453
    inventory = property(read_working_inventory, _write_inventory, None,
454
                         """Inventory for the working copy.""")
455
456
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
457
    def add(self, files, ids=None):
1 by mbp at sourcefrog
import from baz patch-364
458
        """Make files versioned.
459
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
460
        Note that the command line normally calls smart_add instead,
461
        which can automatically recurse.
247 by mbp at sourcefrog
doc
462
1 by mbp at sourcefrog
import from baz patch-364
463
        This puts the files in the Added state, so that they will be
464
        recorded by the next commit.
465
596 by Martin Pool
doc
466
        files
467
            List of paths to add, relative to the base of the tree.
468
469
        ids
470
            If set, use these instead of automatically generated ids.
471
            Must be the same length as the list of files, but may
472
            contain None for ids that are to be autogenerated.
473
254 by Martin Pool
- Doc cleanups from Magnus Therning
474
        TODO: Perhaps have an option to add the ids even if the files do
596 by Martin Pool
doc
475
              not (yet) exist.
1 by mbp at sourcefrog
import from baz patch-364
476
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
477
        TODO: Perhaps yield the ids and paths as they're added.
1 by mbp at sourcefrog
import from baz patch-364
478
        """
479
        # TODO: Re-adding a file that is removed in the working copy
480
        # should probably put it back with the previous ID.
800 by Martin Pool
Merge John's import-speedup branch:
481
        if isinstance(files, basestring):
482
            assert(ids is None or isinstance(ids, basestring))
1 by mbp at sourcefrog
import from baz patch-364
483
            files = [files]
493 by Martin Pool
- Merge aaron's merge command
484
            if ids is not None:
485
                ids = [ids]
486
487
        if ids is None:
488
            ids = [None] * len(files)
489
        else:
490
            assert(len(ids) == len(files))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
491
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
492
        self.lock_write()
493
        try:
494
            inv = self.read_working_inventory()
495
            for f,file_id in zip(files, ids):
496
                if is_control_file(f):
497
                    raise BzrError("cannot add control file %s" % quotefn(f))
498
499
                fp = splitpath(f)
500
501
                if len(fp) == 0:
502
                    raise BzrError("cannot add top-level %r" % f)
503
504
                fullpath = os.path.normpath(self.abspath(f))
505
506
                try:
507
                    kind = file_kind(fullpath)
508
                except OSError:
509
                    # maybe something better?
510
                    raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
511
512
                if kind != 'file' and kind != 'directory':
513
                    raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
514
515
                if file_id is None:
516
                    file_id = gen_file_id(f)
517
                inv.add_path(f, kind=kind, file_id=file_id)
518
519
                mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
520
521
            self._write_inventory(inv)
522
        finally:
523
            self.unlock()
70 by mbp at sourcefrog
Prepare for smart recursive add.
524
            
1 by mbp at sourcefrog
import from baz patch-364
525
176 by mbp at sourcefrog
New cat command contributed by janmar.
526
    def print_file(self, file, revno):
527
        """Print `file` to stdout."""
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
528
        self.lock_read()
529
        try:
530
            tree = self.revision_tree(self.lookup_revision(revno))
531
            # use inventory as it was in that revision
532
            file_id = tree.inventory.path2id(file)
533
            if not file_id:
897 by Martin Pool
- merge john's revision-naming code
534
                raise BzrError("%r is not present in revision %s" % (file, revno))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
535
            tree.print_file(file_id)
536
        finally:
537
            self.unlock()
538
539
1 by mbp at sourcefrog
import from baz patch-364
540
    def remove(self, files, verbose=False):
541
        """Mark nominated files for removal from the inventory.
542
543
        This does not remove their text.  This does not run on 
544
254 by Martin Pool
- Doc cleanups from Magnus Therning
545
        TODO: Refuse to remove modified files unless --force is given?
1 by mbp at sourcefrog
import from baz patch-364
546
254 by Martin Pool
- Doc cleanups from Magnus Therning
547
        TODO: Do something useful with directories.
1 by mbp at sourcefrog
import from baz patch-364
548
254 by Martin Pool
- Doc cleanups from Magnus Therning
549
        TODO: Should this remove the text or not?  Tough call; not
1 by mbp at sourcefrog
import from baz patch-364
550
        removing may be useful and the user can just use use rm, and
551
        is the opposite of add.  Removing it is consistent with most
552
        other tools.  Maybe an option.
553
        """
554
        ## TODO: Normalize names
555
        ## TODO: Remove nested loops; better scalability
800 by Martin Pool
Merge John's import-speedup branch:
556
        if isinstance(files, basestring):
1 by mbp at sourcefrog
import from baz patch-364
557
            files = [files]
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
558
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
559
        self.lock_write()
560
561
        try:
562
            tree = self.working_tree()
563
            inv = tree.inventory
564
565
            # do this before any modifications
566
            for f in files:
567
                fid = inv.path2id(f)
568
                if not fid:
569
                    raise BzrError("cannot remove unversioned file %s" % quotefn(f))
570
                mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
571
                if verbose:
572
                    # having remove it, it must be either ignored or unknown
573
                    if tree.is_ignored(f):
574
                        new_status = 'I'
575
                    else:
576
                        new_status = '?'
577
                    show_status(new_status, inv[fid].kind, quotefn(f))
578
                del inv[fid]
579
580
            self._write_inventory(inv)
581
        finally:
582
            self.unlock()
583
584
612 by Martin Pool
doc
585
    # FIXME: this doesn't need to be a branch method
493 by Martin Pool
- Merge aaron's merge command
586
    def set_inventory(self, new_inventory_list):
800 by Martin Pool
Merge John's import-speedup branch:
587
        from bzrlib.inventory import Inventory, InventoryEntry
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
588
        inv = Inventory(self.get_root_id())
493 by Martin Pool
- Merge aaron's merge command
589
        for path, file_id, parent, kind in new_inventory_list:
590
            name = os.path.basename(path)
591
            if name == "":
592
                continue
593
            inv.add(InventoryEntry(file_id, name, kind, parent))
594
        self._write_inventory(inv)
595
1 by mbp at sourcefrog
import from baz patch-364
596
597
    def unknowns(self):
598
        """Return all unknown files.
599
600
        These are files in the working directory that are not versioned or
601
        control files or ignored.
602
        
603
        >>> b = ScratchBranch(files=['foo', 'foo~'])
604
        >>> list(b.unknowns())
605
        ['foo']
606
        >>> b.add('foo')
607
        >>> list(b.unknowns())
608
        []
609
        >>> b.remove('foo')
610
        >>> list(b.unknowns())
611
        ['foo']
612
        """
613
        return self.working_tree().unknowns()
614
615
905 by Martin Pool
- merge aaron's append_multiple.patch
616
    def append_revision(self, *revision_ids):
617
        for revision_id in revision_ids:
618
            mutter("add {%s} to revision-history" % revision_id)
619
620
        rev_history = self.revision_history()
621
        rev_history.extend(revision_ids)
769 by Martin Pool
- append to branch revision history using AtomicFile
622
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
623
        self.lock_write()
769 by Martin Pool
- append to branch revision history using AtomicFile
624
        try:
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
625
            self.put_controlfile('revision-history', '\n'.join(rev_history))
769 by Martin Pool
- append to branch revision history using AtomicFile
626
        finally:
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
627
            self.unlock()
233 by mbp at sourcefrog
- more output from test.sh
628
629
1182 by Martin Pool
- more disentangling of xml storage format from objects
630
    def get_revision_xml_file(self, revision_id):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
631
        """Return XML file object for revision object."""
632
        if not revision_id or not isinstance(revision_id, basestring):
633
            raise InvalidRevisionId(revision_id)
634
635
        self.lock_read()
636
        try:
637
            try:
638
                return self.revision_store[revision_id]
1185.1.18 by Robert Collins
Lalo Martins remotebranch patch
639
            except (IndexError, KeyError):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
640
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
641
        finally:
642
            self.unlock()
643
644
1182 by Martin Pool
- more disentangling of xml storage format from objects
645
    #deprecated
646
    get_revision_xml = get_revision_xml_file
647
648
1 by mbp at sourcefrog
import from baz patch-364
649
    def get_revision(self, revision_id):
650
        """Return the Revision object for a named revision"""
1182 by Martin Pool
- more disentangling of xml storage format from objects
651
        xml_file = self.get_revision_xml_file(revision_id)
1027 by Martin Pool
- better error message when failing to get revision from store
652
653
        try:
1182 by Martin Pool
- more disentangling of xml storage format from objects
654
            r = bzrlib.xml.serializer_v4.read_revision(xml_file)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
655
        except SyntaxError, e:
656
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
657
                                         [revision_id,
658
                                          str(e)])
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
659
            
1 by mbp at sourcefrog
import from baz patch-364
660
        assert r.revision_id == revision_id
661
        return r
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
662
663
664
    def get_revision_delta(self, revno):
665
        """Return the delta for one revision.
666
667
        The delta is relative to its mainline predecessor, or the
668
        empty tree for revision 1.
669
        """
670
        assert isinstance(revno, int)
671
        rh = self.revision_history()
672
        if not (1 <= revno <= len(rh)):
673
            raise InvalidRevisionNumber(revno)
674
675
        # revno is 1-based; list is 0-based
676
677
        new_tree = self.revision_tree(rh[revno-1])
678
        if revno == 1:
679
            old_tree = EmptyTree()
680
        else:
681
            old_tree = self.revision_tree(rh[revno-2])
682
683
        return compare_trees(old_tree, new_tree)
684
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
685
        
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
686
    def get_revisions(self, revision_ids, pb=None):
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
687
        """Return the Revision object for a set of named revisions"""
688
        from bzrlib.revision import Revision
689
        from bzrlib.xml import unpack_xml
690
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
691
        # TODO: We need to decide what to do here
692
        # we cannot use a generator with a try/finally, because
693
        # you cannot guarantee that the caller will iterate through
694
        # all entries.
695
        # in the past, get_inventory wasn't even wrapped in a
696
        # try/finally locking block.
697
        # We could either lock without the try/finally, or just
698
        # not lock at all. We are reading entries that should
699
        # never be updated.
700
        # I prefer locking with no finally, so that if someone
701
        # asks for a list of revisions, but doesn't consume them,
702
        # that is their problem, and they will suffer the consequences
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
703
        self.lock_read()
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
704
        for xml_file in self.revision_store.get(revision_ids, pb=pb):
705
            try:
706
                r = bzrlib.xml.serializer_v4.read_revision(xml_file)
707
            except SyntaxError, e:
708
                raise bzrlib.errors.BzrError('failed to unpack revision_xml',
709
                                             [revision_id,
710
                                              str(e)])
711
            yield r
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
712
        self.unlock()
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
713
            
672 by Martin Pool
- revision records include the hash of their inventory and
714
    def get_revision_sha1(self, revision_id):
715
        """Hash the stored value of a revision, and return it."""
716
        # In the future, revision entries will be signed. At that
717
        # point, it is probably best *not* to include the signature
718
        # in the revision hash. Because that lets you re-sign
719
        # the revision, (add signatures/remove signatures) and still
720
        # have all hash pointers stay consistent.
721
        # But for now, just hash the contents.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
722
        return bzrlib.osutils.sha_file(self.get_revision_xml(revision_id))
672 by Martin Pool
- revision records include the hash of their inventory and
723
1 by mbp at sourcefrog
import from baz patch-364
724
725
    def get_inventory(self, inventory_id):
726
        """Get Inventory object by hash.
727
254 by Martin Pool
- Doc cleanups from Magnus Therning
728
        TODO: Perhaps for this and similar methods, take a revision
1 by mbp at sourcefrog
import from baz patch-364
729
               parameter which can be either an integer revno or a
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
730
               string hash.
731
        """
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
732
        f = self.get_inventory_xml_file(inventory_id)
1182 by Martin Pool
- more disentangling of xml storage format from objects
733
        return bzrlib.xml.serializer_v4.read_inventory(f)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
734
735
736
    def get_inventory_xml(self, inventory_id):
737
        """Get inventory XML as a file object."""
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
738
        # Shouldn't this have a read-lock around it?
739
        # As well as some sort of trap for missing ids?
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
740
        return self.inventory_store[inventory_id]
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
741
742
    get_inventory_xml_file = get_inventory_xml
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
743
            
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
744
    def get_inventories(self, inventory_ids, pb=None, ignore_missing=False):
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
745
        """Get Inventory objects by id
746
        """
747
        from bzrlib.inventory import Inventory
748
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
749
        # See the discussion in get_revisions for why
750
        # we don't use a try/finally block here
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
751
        self.lock_read()
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
752
        for f in self.inventory_store.get(inventory_ids, pb=pb, ignore_missing=ignore_missing):
753
            if f is not None:
754
                # TODO: Possibly put a try/except around this to handle
755
                # read serialization errors
756
                r = bzrlib.xml.serializer_v4.read_inventory(f)
757
                yield r
758
            elif ignore_missing:
759
                yield None
760
            else:
761
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
762
        self.unlock()
1 by mbp at sourcefrog
import from baz patch-364
763
672 by Martin Pool
- revision records include the hash of their inventory and
764
    def get_inventory_sha1(self, inventory_id):
765
        """Return the sha1 hash of the inventory entry
766
        """
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
767
        return sha_file(self.get_inventory_xml(inventory_id))
672 by Martin Pool
- revision records include the hash of their inventory and
768
1 by mbp at sourcefrog
import from baz patch-364
769
770
    def get_revision_inventory(self, revision_id):
771
        """Return inventory of a past revision."""
820 by Martin Pool
- faster Branch.get_revision_inventory now we know the ids are the same
772
        # bzr 0.0.6 imposes the constraint that the inventory_id
773
        # must be the same as its revision, so this is trivial.
1 by mbp at sourcefrog
import from baz patch-364
774
        if revision_id == None:
800 by Martin Pool
Merge John's import-speedup branch:
775
            from bzrlib.inventory import Inventory
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
776
            return Inventory(self.get_root_id())
1 by mbp at sourcefrog
import from baz patch-364
777
        else:
820 by Martin Pool
- faster Branch.get_revision_inventory now we know the ids are the same
778
            return self.get_inventory(revision_id)
1 by mbp at sourcefrog
import from baz patch-364
779
780
781
    def revision_history(self):
782
        """Return sequence of revision hashes on to this branch.
783
784
        >>> ScratchBranch().revision_history()
785
        []
786
        """
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
787
        self.lock_read()
788
        try:
789
            return [l.rstrip('\r\n') for l in
790
                    self.controlfile('revision-history', 'r').readlines()]
791
        finally:
792
            self.unlock()
1 by mbp at sourcefrog
import from baz patch-364
793
794
622 by Martin Pool
Updated merge patch from Aaron
795
    def common_ancestor(self, other, self_revno=None, other_revno=None):
796
        """
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
797
        >>> from bzrlib.commit import commit
622 by Martin Pool
Updated merge patch from Aaron
798
        >>> sb = ScratchBranch(files=['foo', 'foo~'])
799
        >>> sb.common_ancestor(sb) == (None, None)
800
        True
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
801
        >>> commit(sb, "Committing first revision", verbose=False)
622 by Martin Pool
Updated merge patch from Aaron
802
        >>> sb.common_ancestor(sb)[0]
803
        1
804
        >>> clone = sb.clone()
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
805
        >>> commit(sb, "Committing second revision", verbose=False)
622 by Martin Pool
Updated merge patch from Aaron
806
        >>> sb.common_ancestor(sb)[0]
807
        2
808
        >>> sb.common_ancestor(clone)[0]
809
        1
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
810
        >>> commit(clone, "Committing divergent second revision", 
622 by Martin Pool
Updated merge patch from Aaron
811
        ...               verbose=False)
812
        >>> sb.common_ancestor(clone)[0]
813
        1
814
        >>> sb.common_ancestor(clone) == clone.common_ancestor(sb)
815
        True
816
        >>> sb.common_ancestor(sb) != clone.common_ancestor(clone)
817
        True
818
        >>> clone2 = sb.clone()
819
        >>> sb.common_ancestor(clone2)[0]
820
        2
821
        >>> sb.common_ancestor(clone2, self_revno=1)[0]
822
        1
823
        >>> sb.common_ancestor(clone2, other_revno=1)[0]
824
        1
825
        """
826
        my_history = self.revision_history()
827
        other_history = other.revision_history()
828
        if self_revno is None:
829
            self_revno = len(my_history)
830
        if other_revno is None:
831
            other_revno = len(other_history)
832
        indices = range(min((self_revno, other_revno)))
833
        indices.reverse()
834
        for r in indices:
835
            if my_history[r] == other_history[r]:
836
                return r+1, my_history[r]
837
        return None, None
838
385 by Martin Pool
- New Branch.enum_history method
839
1 by mbp at sourcefrog
import from baz patch-364
840
    def revno(self):
841
        """Return current revision number for this branch.
842
843
        That is equivalent to the number of revisions committed to
844
        this branch.
845
        """
846
        return len(self.revision_history())
847
848
849
    def last_patch(self):
850
        """Return last patch hash, or None if no history.
851
        """
852
        ph = self.revision_history()
853
        if ph:
854
            return ph[-1]
184 by mbp at sourcefrog
pychecker fixups
855
        else:
856
            return None
485 by Martin Pool
- move commit code into its own module
857
858
974.1.27 by aaron.bentley at utoronto
Initial greedy fetch work
859
    def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
628 by Martin Pool
- merge aaron's updated merge/pull code
860
        """
861
        If self and other have not diverged, return a list of the revisions
862
        present in other, but missing from self.
863
864
        >>> from bzrlib.commit import commit
865
        >>> bzrlib.trace.silent = True
866
        >>> br1 = ScratchBranch()
867
        >>> br2 = ScratchBranch()
868
        >>> br1.missing_revisions(br2)
869
        []
870
        >>> commit(br2, "lala!", rev_id="REVISION-ID-1")
871
        >>> br1.missing_revisions(br2)
872
        [u'REVISION-ID-1']
873
        >>> br2.missing_revisions(br1)
874
        []
875
        >>> commit(br1, "lala!", rev_id="REVISION-ID-1")
876
        >>> br1.missing_revisions(br2)
877
        []
878
        >>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
879
        >>> br1.missing_revisions(br2)
880
        [u'REVISION-ID-2A']
881
        >>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
882
        >>> br1.missing_revisions(br2)
883
        Traceback (most recent call last):
884
        DivergedBranches: These branches have diverged.
885
        """
886
        self_history = self.revision_history()
887
        self_len = len(self_history)
888
        other_history = other.revision_history()
889
        other_len = len(other_history)
890
        common_index = min(self_len, other_len) -1
891
        if common_index >= 0 and \
892
            self_history[common_index] != other_history[common_index]:
893
            raise DivergedBranches(self, other)
685 by Martin Pool
- add -r option to the branch command
894
895
        if stop_revision is None:
896
            stop_revision = other_len
897
        elif stop_revision > other_len:
1103 by Martin Pool
- some pychecker cleanups
898
            raise bzrlib.errors.NoSuchRevision(self, stop_revision)
685 by Martin Pool
- add -r option to the branch command
899
        
900
        return other_history[self_len:stop_revision]
901
902
974.1.28 by aaron.bentley at utoronto
factored install_revisions out of update_revisions, updated test cases for greedy_fetch
903
    def update_revisions(self, other, stop_revision=None):
663 by Martin Pool
doc
904
        """Pull in all new revisions from other branch.
628 by Martin Pool
- merge aaron's updated merge/pull code
905
        """
974.1.33 by aaron.bentley at utoronto
Added greedy_fetch to update_revisions
906
        from bzrlib.fetch import greedy_fetch
974.1.74 by Aaron Bentley
Made pull work after remote branch has merged latest revision
907
        from bzrlib.revision import get_intervening_revisions
1110 by Martin Pool
- merge aaron's merge improvements:
908
909
        pb = bzrlib.ui.ui_factory.progress_bar()
1092.1.35 by Robert Collins
merge from mpool up to rev 1110
910
        pb.update('comparing histories')
974.1.75 by Aaron Bentley
Sped up pull by copying locally first
911
        if stop_revision is None:
912
            other_revision = other.last_patch()
913
        else:
914
            other_revision = other.lookup_revision(stop_revision)
915
        count = greedy_fetch(self, other, other_revision, pb)[0]
974.1.74 by Aaron Bentley
Made pull work after remote branch has merged latest revision
916
        try:
917
            revision_ids = self.missing_revisions(other, stop_revision)
918
        except DivergedBranches, e:
919
            try:
920
                revision_ids = get_intervening_revisions(self.last_patch(), 
974.1.75 by Aaron Bentley
Sped up pull by copying locally first
921
                                                         other_revision, self)
974.1.74 by Aaron Bentley
Made pull work after remote branch has merged latest revision
922
                assert self.last_patch() not in revision_ids
923
            except bzrlib.errors.NotAncestor:
924
                raise e
1110 by Martin Pool
- merge aaron's merge improvements:
925
974.1.28 by aaron.bentley at utoronto
factored install_revisions out of update_revisions, updated test cases for greedy_fetch
926
        self.append_revision(*revision_ids)
1116 by Martin Pool
- fix a few errors in new merge code
927
        pb.clear()
1104 by Martin Pool
- Add a simple UIFactory
928
1110 by Martin Pool
- merge aaron's merge improvements:
929
    def install_revisions(self, other, revision_ids, pb):
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
930
        # We are going to iterate this many times, so make sure
931
        # that it is a list, and not a generator
932
        revision_ids = list(revision_ids)
790 by Martin Pool
Merge from aaron:
933
        if hasattr(other.revision_store, "prefetch"):
934
            other.revision_store.prefetch(revision_ids)
935
        if hasattr(other.inventory_store, "prefetch"):
936
            other.inventory_store.prefetch(inventory_ids)
1110 by Martin Pool
- merge aaron's merge improvements:
937
938
        if pb is None:
939
            pb = bzrlib.ui.ui_factory.progress_bar()
790 by Martin Pool
Merge from aaron:
940
                
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
941
        # This entire next section is generally done
942
        # with either generators, or bulk updates
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
943
        inventories = other.get_inventories(revision_ids, ignore_missing=True)
800 by Martin Pool
Merge John's import-speedup branch:
944
        needed_texts = set()
907.1.37 by John Arbash Meinel
Upgraded branch.get_revisions() to be a generator, simplified branch.update_revisions, removing unneeded waste.
945
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
946
        failures = set()
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
947
        good_revisions = set()
948
        for i, (inv, rev_id) in enumerate(zip(inventories, revision_ids)):
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
949
            pb.update('fetching revision', i+1, len(revision_ids))
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
950
951
            # We don't really need to get the revision here, because
952
            # the only thing we needed was the inventory_id, which now
953
            # is (by design) identical to the revision_id
954
            # try:
955
            #     rev = other.get_revision(rev_id)
956
            # except bzrlib.errors.NoSuchRevision:
957
            #     failures.add(rev_id)
958
            #     continue
959
960
            if inv is None:
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
961
                failures.add(rev_id)
962
                continue
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
963
            else:
964
                good_revisions.add(rev_id)
1110 by Martin Pool
- merge aaron's merge improvements:
965
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
966
            text_ids = []
628 by Martin Pool
- merge aaron's updated merge/pull code
967
            for key, entry in inv.iter_entries():
968
                if entry.text_id is None:
969
                    continue
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
970
                text_ids.append(entry.text_id)
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
971
907.1.36 by John Arbash Meinel
Moving the multi-get functionality higher up into the Branch class.
972
            has_ids = self.text_store.has(text_ids)
973
            for has, text_id in zip(has_ids, text_ids):
974
                if not has:
975
                    needed_texts.add(text_id)
670 by Martin Pool
- Show progress while branching
976
1092.1.35 by Robert Collins
merge from mpool up to rev 1110
977
        pb.clear()
670 by Martin Pool
- Show progress while branching
978
                    
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
979
        count, cp_fail = self.text_store.copy_multi(other.text_store, 
980
                                                    needed_texts)
1116 by Martin Pool
- fix a few errors in new merge code
981
        #print "Added %d texts." % count 
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
982
        count, cp_fail = self.inventory_store.copy_multi(other.inventory_store,
983
                                                         good_revisions)
1116 by Martin Pool
- fix a few errors in new merge code
984
        #print "Added %d inventories." % count 
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
985
        count, cp_fail = self.revision_store.copy_multi(other.revision_store, 
1185.11.1 by John Arbash Meinel
(broken) Transport work is merged in. Tests do not pass yet.
986
                                                          good_revisions,
974.1.30 by aaron.bentley at utoronto
Changed copy_multi to permit failure and return a tuple, tested missing required revisions
987
                                                          permit_failure=True)
988
        assert len(cp_fail) == 0 
989
        return count, failures
974.1.28 by aaron.bentley at utoronto
factored install_revisions out of update_revisions, updated test cases for greedy_fetch
990
       
1110 by Martin Pool
- merge aaron's merge improvements:
991
485 by Martin Pool
- move commit code into its own module
992
    def commit(self, *args, **kw):
993
        from bzrlib.commit import commit
994
        commit(self, *args, **kw)
184 by mbp at sourcefrog
pychecker fixups
995
        
1 by mbp at sourcefrog
import from baz patch-364
996
897 by Martin Pool
- merge john's revision-naming code
997
    def lookup_revision(self, revision):
998
        """Return the revision identifier for a given revision information."""
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
999
        revno, info = self._get_revision_info(revision)
897 by Martin Pool
- merge john's revision-naming code
1000
        return info
1001
1105 by Martin Pool
- expose 'find-merge-base' as a new expert command,
1002
1003
    def revision_id_to_revno(self, revision_id):
1004
        """Given a revision id, return its revno"""
1005
        history = self.revision_history()
1006
        try:
1007
            return history.index(revision_id) + 1
1008
        except ValueError:
1009
            raise bzrlib.errors.NoSuchRevision(self, revision_id)
1010
1011
897 by Martin Pool
- merge john's revision-naming code
1012
    def get_revision_info(self, revision):
1013
        """Return (revno, revision id) for revision identifier.
1014
1015
        revision can be an integer, in which case it is assumed to be revno (though
1016
            this will translate negative values into positive ones)
1017
        revision can also be a string, in which case it is parsed for something like
1018
            'date:' or 'revid:' etc.
1019
        """
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1020
        revno, rev_id = self._get_revision_info(revision)
1021
        if revno is None:
1022
            raise bzrlib.errors.NoSuchRevision(self, revision)
1023
        return revno, rev_id
1024
1025
    def get_rev_id(self, revno, history=None):
1026
        """Find the revision id of the specified revno."""
1027
        if revno == 0:
1028
            return None
1029
        if history is None:
1030
            history = self.revision_history()
1031
        elif revno <= 0 or revno > len(history):
1032
            raise bzrlib.errors.NoSuchRevision(self, revno)
1033
        return history[revno - 1]
1034
1035
    def _get_revision_info(self, revision):
1036
        """Return (revno, revision id) for revision specifier.
1037
1038
        revision can be an integer, in which case it is assumed to be revno
1039
        (though this will translate negative values into positive ones)
1040
        revision can also be a string, in which case it is parsed for something
1041
        like 'date:' or 'revid:' etc.
1042
1043
        A revid is always returned.  If it is None, the specifier referred to
1044
        the null revision.  If the revid does not occur in the revision
1045
        history, revno will be None.
1046
        """
1047
        
897 by Martin Pool
- merge john's revision-naming code
1048
        if revision is None:
1049
            return 0, None
1050
        revno = None
1051
        try:# Convert to int if possible
1052
            revision = int(revision)
1053
        except ValueError:
1054
            pass
1055
        revs = self.revision_history()
1056
        if isinstance(revision, int):
1057
            if revision < 0:
1058
                revno = len(revs) + revision + 1
1059
            else:
1060
                revno = revision
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1061
            rev_id = self.get_rev_id(revno, revs)
897 by Martin Pool
- merge john's revision-naming code
1062
        elif isinstance(revision, basestring):
1063
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
1064
                if revision.startswith(prefix):
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1065
                    result = func(self, revs, revision)
1066
                    if len(result) > 1:
1067
                        revno, rev_id = result
1068
                    else:
1069
                        revno = result[0]
1070
                        rev_id = self.get_rev_id(revno, revs)
897 by Martin Pool
- merge john's revision-naming code
1071
                    break
1072
            else:
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1073
                raise BzrError('No namespace registered for string: %r' %
1074
                               revision)
1075
        else:
1076
            raise TypeError('Unhandled revision type %s' % revision)
897 by Martin Pool
- merge john's revision-naming code
1077
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1078
        if revno is None:
1079
            if rev_id is None:
1080
                raise bzrlib.errors.NoSuchRevision(self, revision)
1081
        return revno, rev_id
897 by Martin Pool
- merge john's revision-naming code
1082
1083
    def _namespace_revno(self, revs, revision):
1084
        """Lookup a revision by revision number"""
1085
        assert revision.startswith('revno:')
1086
        try:
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1087
            return (int(revision[6:]),)
897 by Martin Pool
- merge john's revision-naming code
1088
        except ValueError:
1089
            return None
1090
    REVISION_NAMESPACES['revno:'] = _namespace_revno
1091
1092
    def _namespace_revid(self, revs, revision):
1093
        assert revision.startswith('revid:')
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1094
        rev_id = revision[len('revid:'):]
897 by Martin Pool
- merge john's revision-naming code
1095
        try:
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1096
            return revs.index(rev_id) + 1, rev_id
897 by Martin Pool
- merge john's revision-naming code
1097
        except ValueError:
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1098
            return None, rev_id
897 by Martin Pool
- merge john's revision-naming code
1099
    REVISION_NAMESPACES['revid:'] = _namespace_revid
1100
1101
    def _namespace_last(self, revs, revision):
1102
        assert revision.startswith('last:')
1103
        try:
1104
            offset = int(revision[5:])
1105
        except ValueError:
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1106
            return (None,)
897 by Martin Pool
- merge john's revision-naming code
1107
        else:
1108
            if offset <= 0:
1109
                raise BzrError('You must supply a positive value for --revision last:XXX')
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1110
            return (len(revs) - offset + 1,)
897 by Martin Pool
- merge john's revision-naming code
1111
    REVISION_NAMESPACES['last:'] = _namespace_last
1112
1113
    def _namespace_tag(self, revs, revision):
1114
        assert revision.startswith('tag:')
1115
        raise BzrError('tag: namespace registered, but not implemented.')
1116
    REVISION_NAMESPACES['tag:'] = _namespace_tag
1117
1118
    def _namespace_date(self, revs, revision):
1119
        assert revision.startswith('date:')
1120
        import datetime
1121
        # Spec for date revisions:
1122
        #   date:value
1123
        #   value can be 'yesterday', 'today', 'tomorrow' or a YYYY-MM-DD string.
1124
        #   it can also start with a '+/-/='. '+' says match the first
1125
        #   entry after the given date. '-' is match the first entry before the date
1126
        #   '=' is match the first entry after, but still on the given date.
1127
        #
1128
        #   +2005-05-12 says find the first matching entry after May 12th, 2005 at 0:00
1129
        #   -2005-05-12 says find the first matching entry before May 12th, 2005 at 0:00
1130
        #   =2005-05-12 says find the first match after May 12th, 2005 at 0:00 but before
1131
        #       May 13th, 2005 at 0:00
1132
        #
1133
        #   So the proper way of saying 'give me all entries for today' is:
1134
        #       -r {date:+today}:{date:-tomorrow}
1135
        #   The default is '=' when not supplied
1136
        val = revision[5:]
1137
        match_style = '='
1138
        if val[:1] in ('+', '-', '='):
1139
            match_style = val[:1]
1140
            val = val[1:]
1141
1142
        today = datetime.datetime.today().replace(hour=0,minute=0,second=0,microsecond=0)
1143
        if val.lower() == 'yesterday':
1144
            dt = today - datetime.timedelta(days=1)
1145
        elif val.lower() == 'today':
1146
            dt = today
1147
        elif val.lower() == 'tomorrow':
1148
            dt = today + datetime.timedelta(days=1)
1149
        else:
901 by Martin Pool
- fix missing import
1150
            import re
897 by Martin Pool
- merge john's revision-naming code
1151
            # This should be done outside the function to avoid recompiling it.
1152
            _date_re = re.compile(
1153
                    r'(?P<date>(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d))?'
1154
                    r'(,|T)?\s*'
1155
                    r'(?P<time>(?P<hour>\d\d):(?P<minute>\d\d)(:(?P<second>\d\d))?)?'
1156
                )
1157
            m = _date_re.match(val)
1158
            if not m or (not m.group('date') and not m.group('time')):
1159
                raise BzrError('Invalid revision date %r' % revision)
1160
1161
            if m.group('date'):
1162
                year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day'))
1163
            else:
1164
                year, month, day = today.year, today.month, today.day
1165
            if m.group('time'):
1166
                hour = int(m.group('hour'))
1167
                minute = int(m.group('minute'))
1168
                if m.group('second'):
1169
                    second = int(m.group('second'))
1170
                else:
1171
                    second = 0
1172
            else:
1173
                hour, minute, second = 0,0,0
1174
1175
            dt = datetime.datetime(year=year, month=month, day=day,
1176
                    hour=hour, minute=minute, second=second)
1177
        first = dt
1178
        last = None
1179
        reversed = False
1180
        if match_style == '-':
1181
            reversed = True
1182
        elif match_style == '=':
1183
            last = dt + datetime.timedelta(days=1)
1184
1185
        if reversed:
1186
            for i in range(len(revs)-1, -1, -1):
1187
                r = self.get_revision(revs[i])
1188
                # TODO: Handle timezone.
1189
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1190
                if first >= dt and (last is None or dt >= last):
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1191
                    return (i+1,)
897 by Martin Pool
- merge john's revision-naming code
1192
        else:
1193
            for i in range(len(revs)):
1194
                r = self.get_revision(revs[i])
1195
                # TODO: Handle timezone.
1196
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1197
                if first <= dt and (last is None or dt <= last):
974.2.7 by aaron.bentley at utoronto
Merged from bzr.24
1198
                    return (i+1,)
897 by Martin Pool
- merge john's revision-naming code
1199
    REVISION_NAMESPACES['date:'] = _namespace_date
1 by mbp at sourcefrog
import from baz patch-364
1200
974.1.81 by Aaron Bentley
Added ancestor revision namepsace
1201
1202
    def _namespace_ancestor(self, revs, revision):
1203
        from revision import common_ancestor, MultipleRevisionSources
1204
        other_branch = find_branch(_trim_namespace('ancestor', revision))
1205
        revision_a = self.last_patch()
1206
        revision_b = other_branch.last_patch()
1207
        for r, b in ((revision_a, self), (revision_b, other_branch)):
1208
            if r is None:
1209
                raise bzrlib.errors.NoCommits(b)
1210
        revision_source = MultipleRevisionSources(self, other_branch)
1211
        result = common_ancestor(revision_a, revision_b, revision_source)
1212
        try:
1213
            revno = self.revision_id_to_revno(result)
1214
        except bzrlib.errors.NoSuchRevision:
1215
            revno = None
1216
        return revno,result
1217
        
1218
1219
    REVISION_NAMESPACES['ancestor:'] = _namespace_ancestor
1220
1 by mbp at sourcefrog
import from baz patch-364
1221
    def revision_tree(self, revision_id):
1222
        """Return Tree for a revision on this branch.
1223
1224
        `revision_id` may be None for the null revision, in which case
1225
        an `EmptyTree` is returned."""
529 by Martin Pool
todo
1226
        # TODO: refactor this to use an existing revision object
1227
        # so we don't need to read it in twice.
1 by mbp at sourcefrog
import from baz patch-364
1228
        if revision_id == None:
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
1229
            return EmptyTree()
1 by mbp at sourcefrog
import from baz patch-364
1230
        else:
1231
            inv = self.get_revision_inventory(revision_id)
1232
            return RevisionTree(self.text_store, inv)
1233
1234
1235
    def working_tree(self):
1236
        """Return a `Tree` for the working copy."""
1185.2.2 by Lalo Martins
cleaning up and refactoring the branch module.
1237
        from bzrlib.workingtree import WorkingTree
907.1.5 by John Arbash Meinel
Some more work, including ScratchBranch changes.
1238
        # TODO: In the future, WorkingTree should utilize Transport
1239
        return WorkingTree(self._transport.base, self.read_working_inventory())
1 by mbp at sourcefrog
import from baz patch-364
1240
1241
1242
    def basis_tree(self):
1243
        """Return `Tree` object for last revision.
1244
1245
        If there are no revisions yet, return an `EmptyTree`.
1246
        """
1247
        r = self.last_patch()
1248
        if r == None:
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
1249
            return EmptyTree()
1 by mbp at sourcefrog
import from baz patch-364
1250
        else:
1251
            return RevisionTree(self.text_store, self.get_revision_inventory(r))
1252
1253
1254
168 by mbp at sourcefrog
new "rename" command
1255
    def rename_one(self, from_rel, to_rel):
309 by Martin Pool
doc
1256
        """Rename one file.
1257
1258
        This can change the directory or the filename or both.
353 by Martin Pool
- Per-branch locks in read and write modes.
1259
        """
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1260
        self.lock_write()
171 by mbp at sourcefrog
better error message when working file rename fails
1261
        try:
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1262
            tree = self.working_tree()
1263
            inv = tree.inventory
1264
            if not tree.has_filename(from_rel):
1265
                raise BzrError("can't rename: old working file %r does not exist" % from_rel)
1266
            if tree.has_filename(to_rel):
1267
                raise BzrError("can't rename: new working file %r already exists" % to_rel)
1268
1269
            file_id = inv.path2id(from_rel)
1270
            if file_id == None:
1271
                raise BzrError("can't rename: old name %r is not versioned" % from_rel)
1272
1273
            if inv.path2id(to_rel):
1274
                raise BzrError("can't rename: new name %r is already versioned" % to_rel)
1275
1276
            to_dir, to_tail = os.path.split(to_rel)
1277
            to_dir_id = inv.path2id(to_dir)
1278
            if to_dir_id == None and to_dir != '':
1279
                raise BzrError("can't determine destination directory id for %r" % to_dir)
1280
1281
            mutter("rename_one:")
1282
            mutter("  file_id    {%s}" % file_id)
1283
            mutter("  from_rel   %r" % from_rel)
1284
            mutter("  to_rel     %r" % to_rel)
1285
            mutter("  to_dir     %r" % to_dir)
1286
            mutter("  to_dir_id  {%s}" % to_dir_id)
1287
1288
            inv.rename(file_id, to_dir_id, to_tail)
1289
1290
            from_abs = self.abspath(from_rel)
1291
            to_abs = self.abspath(to_rel)
1292
            try:
1293
                os.rename(from_abs, to_abs)
1294
            except OSError, e:
1295
                raise BzrError("failed to rename %r to %r: %s"
1296
                        % (from_abs, to_abs, e[1]),
1297
                        ["rename rolled back"])
1298
1299
            self._write_inventory(inv)
1300
        finally:
1301
            self.unlock()
1302
1303
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
1304
    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
1305
        """Rename files.
1306
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
1307
        to_name must exist as a versioned directory.
1308
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
1309
        If to_name exists and is a directory, the files are moved into
1310
        it, keeping their old names.  If it is a directory, 
1311
1312
        Note that to_name is only the last component of the new name;
1313
        this doesn't change the directory.
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1314
1315
        This returns a list of (from_path, to_path) pairs for each
1316
        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
1317
        """
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1318
        result = []
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1319
        self.lock_write()
1320
        try:
1321
            ## TODO: Option to move IDs only
1322
            assert not isinstance(from_paths, basestring)
1323
            tree = self.working_tree()
1324
            inv = tree.inventory
1325
            to_abs = self.abspath(to_name)
1326
            if not isdir(to_abs):
1327
                raise BzrError("destination %r is not a directory" % to_abs)
1328
            if not tree.has_filename(to_name):
1329
                raise BzrError("destination %r not in working directory" % to_abs)
1330
            to_dir_id = inv.path2id(to_name)
1331
            if to_dir_id == None and to_name != '':
1332
                raise BzrError("destination %r is not a versioned directory" % to_name)
1333
            to_dir_ie = inv[to_dir_id]
1334
            if to_dir_ie.kind not in ('directory', 'root_directory'):
1335
                raise BzrError("destination %r is not a directory" % to_abs)
1336
1337
            to_idpath = inv.get_idpath(to_dir_id)
1338
1339
            for f in from_paths:
1340
                if not tree.has_filename(f):
1341
                    raise BzrError("%r does not exist in working tree" % f)
1342
                f_id = inv.path2id(f)
1343
                if f_id == None:
1344
                    raise BzrError("%r is not versioned" % f)
1345
                name_tail = splitpath(f)[-1]
1346
                dest_path = appendpath(to_name, name_tail)
1347
                if tree.has_filename(dest_path):
1348
                    raise BzrError("destination %r already exists" % dest_path)
1349
                if f_id in to_idpath:
1350
                    raise BzrError("can't move %r to a subdirectory of itself" % f)
1351
1352
            # OK, so there's a race here, it's possible that someone will
1353
            # create a file in this interval and then the rename might be
1354
            # left half-done.  But we should have caught most problems.
1355
1356
            for f in from_paths:
1357
                name_tail = splitpath(f)[-1]
1358
                dest_path = appendpath(to_name, name_tail)
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1359
                result.append((f, dest_path))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
1360
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1361
                try:
1362
                    os.rename(self.abspath(f), self.abspath(dest_path))
1363
                except OSError, e:
1364
                    raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1365
                            ["rename rolled back"])
1366
1367
            self._write_inventory(inv)
1368
        finally:
1369
            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
1370
1131 by Martin Pool
- remove more extraneous print statements from Branch.move
1371
        return result
1372
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
1373
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1374
    def revert(self, filenames, old_tree=None, backups=True):
778 by Martin Pool
- simple revert of text files
1375
        """Restore selected files to the versions from a previous tree.
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1376
1377
        backups
1378
            If true (default) backups are made of files before
1379
            they're renamed.
778 by Martin Pool
- simple revert of text files
1380
        """
1381
        from bzrlib.errors import NotVersionedError, BzrError
1382
        from bzrlib.atomicfile import AtomicFile
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1383
        from bzrlib.osutils import backup_file
778 by Martin Pool
- simple revert of text files
1384
        
1385
        inv = self.read_working_inventory()
1386
        if old_tree is None:
1387
            old_tree = self.basis_tree()
1388
        old_inv = old_tree.inventory
1389
1390
        nids = []
1391
        for fn in filenames:
1392
            file_id = inv.path2id(fn)
1393
            if not file_id:
1394
                raise NotVersionedError("not a versioned file", fn)
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1395
            if not old_inv.has_id(file_id):
1396
                raise BzrError("file not present in old tree", fn, file_id)
778 by Martin Pool
- simple revert of text files
1397
            nids.append((fn, file_id))
1398
            
1399
        # TODO: Rename back if it was previously at a different location
1400
1401
        # TODO: If given a directory, restore the entire contents from
1402
        # the previous version.
1403
1404
        # TODO: Make a backup to a temporary file.
1405
1406
        # TODO: If the file previously didn't exist, delete it?
1407
        for fn, file_id in nids:
782 by Martin Pool
- Branch.revert copies files to backups before reverting them
1408
            backup_file(fn)
1409
            
778 by Martin Pool
- simple revert of text files
1410
            f = AtomicFile(fn, 'wb')
1411
            try:
1412
                f.write(old_tree.get_file(file_id).read())
1413
                f.commit()
1414
            finally:
1415
                f.close()
1416
1417
815 by Martin Pool
- track pending-merges
1418
    def pending_merges(self):
1419
        """Return a list of pending merges.
1420
1421
        These are revisions that have been merged into the working
1422
        directory but not yet committed.
1423
        """
907.1.2 by John Arbash Meinel
Working on making Branch() do all of it's work over a Transport.
1424
        cfn = self._rel_controlfilename('pending-merges')
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
1425
        if not self._transport.has(cfn):
815 by Martin Pool
- track pending-merges
1426
            return []
1427
        p = []
1428
        for l in self.controlfile('pending-merges', 'r').readlines():
1429
            p.append(l.rstrip('\n'))
1430
        return p
1431
1432
907.1.4 by John Arbash Meinel
Add pending_merge can take multiple entries.
1433
    def add_pending_merge(self, *revision_ids):
815 by Martin Pool
- track pending-merges
1434
        from bzrlib.revision import validate_revision_id
1435
907.1.4 by John Arbash Meinel
Add pending_merge can take multiple entries.
1436
        for rev_id in revision_ids:
1437
            validate_revision_id(rev_id)
815 by Martin Pool
- track pending-merges
1438
1439
        p = self.pending_merges()
907.1.4 by John Arbash Meinel
Add pending_merge can take multiple entries.
1440
        updated = False
1441
        for rev_id in revision_ids:
1442
            if rev_id in p:
1443
                continue
1444
            p.append(rev_id)
1445
            updated = True
1446
        if updated:
1447
            self.set_pending_merges(p)
815 by Martin Pool
- track pending-merges
1448
1449
    def set_pending_merges(self, rev_list):
1450
        self.lock_write()
1451
        try:
907.1.50 by John Arbash Meinel
Removed encode/decode from Transport.put/get, added more exceptions that can be thrown.
1452
            self.put_controlfile('pending-merges', '\n'.join(rev_list))
815 by Martin Pool
- track pending-merges
1453
        finally:
1454
            self.unlock()
1455
1456
1149 by Martin Pool
- make get_parent() be a method of Branch; add simple tests for it
1457
    def get_parent(self):
1458
        """Return the parent location of the branch.
1459
1460
        This is the default location for push/pull/missing.  The usual
1461
        pattern is that the user can override it by specifying a
1462
        location.
1463
        """
1464
        import errno
1465
        _locs = ['parent', 'pull', 'x-pull']
1466
        for l in _locs:
1467
            try:
1468
                return self.controlfile(l, 'r').read().strip('\n')
1469
            except IOError, e:
1470
                if e.errno != errno.ENOENT:
1471
                    raise
1472
        return None
1473
1150 by Martin Pool
- add new Branch.set_parent and tests
1474
1475
    def set_parent(self, url):
1476
        # TODO: Maybe delete old location files?
1477
        from bzrlib.atomicfile import AtomicFile
1478
        self.lock_write()
1479
        try:
1480
            f = AtomicFile(self.controlfilename('parent'))
1481
            try:
1482
                f.write(url + '\n')
1483
                f.commit()
1484
            finally:
1485
                f.close()
1486
        finally:
1487
            self.unlock()
1488
974.1.54 by aaron.bentley at utoronto
Fixed the revno bug in log
1489
    def check_revno(self, revno):
1490
        """\
1491
        Check whether a revno corresponds to any revision.
1492
        Zero (the NULL revision) is considered valid.
1493
        """
1494
        if revno != 0:
1495
            self.check_real_revno(revno)
1496
            
1497
    def check_real_revno(self, revno):
1498
        """\
1499
        Check whether a revno corresponds to a real revision.
1500
        Zero (the NULL revision) is considered invalid
1501
        """
1502
        if revno < 1 or revno > self.revno():
1503
            raise InvalidRevisionNumber(revno)
1504
        
1149 by Martin Pool
- make get_parent() be a method of Branch; add simple tests for it
1505
        
1506
1 by mbp at sourcefrog
import from baz patch-364
1507
1508
class ScratchBranch(Branch):
1509
    """Special test class: a branch that cleans up after itself.
1510
1511
    >>> b = ScratchBranch()
1512
    >>> isdir(b.base)
1513
    True
1514
    >>> bd = b.base
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1515
    >>> b.destroy()
1 by mbp at sourcefrog
import from baz patch-364
1516
    >>> isdir(bd)
1517
    False
1518
    """
622 by Martin Pool
Updated merge patch from Aaron
1519
    def __init__(self, files=[], dirs=[], base=None):
1 by mbp at sourcefrog
import from baz patch-364
1520
        """Make a test branch.
1521
1522
        This creates a temporary directory and runs init-tree in it.
1523
1524
        If any files are listed, they are created in the working copy.
1525
        """
800 by Martin Pool
Merge John's import-speedup branch:
1526
        from tempfile import mkdtemp
622 by Martin Pool
Updated merge patch from Aaron
1527
        init = False
1528
        if base is None:
800 by Martin Pool
Merge John's import-speedup branch:
1529
            base = mkdtemp()
622 by Martin Pool
Updated merge patch from Aaron
1530
            init = True
1531
        Branch.__init__(self, base, init=init)
100 by mbp at sourcefrog
- add test case for ignore files
1532
        for d in dirs:
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
1533
            self._transport.mkdir(d)
100 by mbp at sourcefrog
- add test case for ignore files
1534
            
1 by mbp at sourcefrog
import from baz patch-364
1535
        for f in files:
907.1.8 by John Arbash Meinel
Changed the format for abspath. Updated branch to use a hidden _transport
1536
            self._transport.put(f, 'content of %s' % f)
1 by mbp at sourcefrog
import from baz patch-364
1537
1538
622 by Martin Pool
Updated merge patch from Aaron
1539
    def clone(self):
1540
        """
1541
        >>> orig = ScratchBranch(files=["file1", "file2"])
1542
        >>> clone = orig.clone()
1543
        >>> os.path.samefile(orig.base, clone.base)
1544
        False
1545
        >>> os.path.isfile(os.path.join(clone.base, "file1"))
1546
        True
1547
        """
800 by Martin Pool
Merge John's import-speedup branch:
1548
        from shutil import copytree
1549
        from tempfile import mkdtemp
1550
        base = mkdtemp()
622 by Martin Pool
Updated merge patch from Aaron
1551
        os.rmdir(base)
800 by Martin Pool
Merge John's import-speedup branch:
1552
        copytree(self.base, base, symlinks=True)
622 by Martin Pool
Updated merge patch from Aaron
1553
        return ScratchBranch(base=base)
1149 by Martin Pool
- make get_parent() be a method of Branch; add simple tests for it
1554
1555
622 by Martin Pool
Updated merge patch from Aaron
1556
        
1 by mbp at sourcefrog
import from baz patch-364
1557
    def __del__(self):
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1558
        self.destroy()
1559
1560
    def destroy(self):
1 by mbp at sourcefrog
import from baz patch-364
1561
        """Destroy the test branch, removing the scratch directory."""
800 by Martin Pool
Merge John's import-speedup branch:
1562
        from shutil import rmtree
163 by mbp at sourcefrog
merge win32 portability fixes
1563
        try:
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
1564
            if self.base:
1565
                mutter("delete ScratchBranch %s" % self.base)
800 by Martin Pool
Merge John's import-speedup branch:
1566
                rmtree(self.base)
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1567
        except OSError, e:
163 by mbp at sourcefrog
merge win32 portability fixes
1568
            # Work around for shutil.rmtree failing on Windows when
1569
            # readonly files are encountered
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
1570
            mutter("hit exception in destroying ScratchBranch: %s" % e)
163 by mbp at sourcefrog
merge win32 portability fixes
1571
            for root, dirs, files in os.walk(self.base, topdown=False):
1572
                for name in files:
1573
                    os.chmod(os.path.join(root, name), 0700)
800 by Martin Pool
Merge John's import-speedup branch:
1574
            rmtree(self.base)
907.1.19 by John Arbash Meinel
Updated ScratchBranch and Branch.base, All Tests PASS !!!
1575
        self._transport = None
1 by mbp at sourcefrog
import from baz patch-364
1576
1577
    
1578
1579
######################################################################
1580
# predicates
1581
1582
1583
def is_control_file(filename):
1584
    ## FIXME: better check
1585
    filename = os.path.normpath(filename)
1586
    while filename != '':
1587
        head, tail = os.path.split(filename)
1588
        ## mutter('check %r for control file' % ((head, tail), ))
1589
        if tail == bzrlib.BZRDIR:
1590
            return True
70 by mbp at sourcefrog
Prepare for smart recursive add.
1591
        if filename == head:
1592
            break
1 by mbp at sourcefrog
import from baz patch-364
1593
        filename = head
1594
    return False
1595
1596
1597
70 by mbp at sourcefrog
Prepare for smart recursive add.
1598
def gen_file_id(name):
1 by mbp at sourcefrog
import from baz patch-364
1599
    """Return new file id.
1600
1601
    This should probably generate proper UUIDs, but for the moment we
1602
    cope with just randomness because running uuidgen every time is
1603
    slow."""
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1604
    import re
800 by Martin Pool
Merge John's import-speedup branch:
1605
    from binascii import hexlify
1606
    from time import time
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1607
1608
    # get last component
70 by mbp at sourcefrog
Prepare for smart recursive add.
1609
    idx = name.rfind('/')
1610
    if idx != -1:
1611
        name = name[idx+1 : ]
262 by Martin Pool
- gen_file_id: break the file on either / or \ when looking
1612
    idx = name.rfind('\\')
1613
    if idx != -1:
1614
        name = name[idx+1 : ]
70 by mbp at sourcefrog
Prepare for smart recursive add.
1615
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1616
    # make it not a hidden file
70 by mbp at sourcefrog
Prepare for smart recursive add.
1617
    name = name.lstrip('.')
1618
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
1619
    # remove any wierd characters; we don't escape them but rather
1620
    # just pull them out
1621
    name = re.sub(r'[^\w.]', '', name)
1622
190 by mbp at sourcefrog
64 bits of randomness in file/revision ids
1623
    s = hexlify(rand_bytes(8))
800 by Martin Pool
Merge John's import-speedup branch:
1624
    return '-'.join((name, compact_date(time()), s))
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
1625
1626
1627
def gen_root_id():
1628
    """Return a new tree-root file id."""
1629
    return gen_file_id('TREE_ROOT')
1630
1092.1.34 by Robert Collins
unbreak cmd_branch now that something tests the core of it..
1631
1632
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
1633
    """Copy branch_from into the existing directory to_location.
1634
1151 by Martin Pool
- assertions and documentation for copy_branch
1635
    revision
1636
        If not None, only revisions up to this point will be copied.
1637
        The head of the new branch will be that revision.
1638
1639
    to_location
1640
        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
1641
    """
1642
    from bzrlib.merge import merge
1151 by Martin Pool
- assertions and documentation for copy_branch
1643
1644
    assert isinstance(branch_from, Branch)
1645
    assert isinstance(to_location, basestring)
1646
    
1092.1.33 by Robert Collins
pull the important stuff out of cmd_branch.run to branch.copy_branch
1647
    br_to = Branch(to_location, init=True)
1648
    br_to.set_root_id(branch_from.get_root_id())
1649
    if revision is None:
1650
        revno = branch_from.revno()
1651
    else:
1652
        revno, rev_id = branch_from.get_revision_info(revision)
1653
    br_to.update_revisions(branch_from, stop_revision=revno)
1654
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1655
          check_clean=False, ignore_zero=True)
1185.2.3 by Lalo Martins
unifying 'base' (from Branch) and 'baseurl' (from RemoteBranch) attributes;
1656
    br_to.set_parent(branch_from.base)
974.1.81 by Aaron Bentley
Added ancestor revision namepsace
1657
    return br_to
1092.1.33 by Robert Collins
pull the important stuff out of cmd_branch.run to branch.copy_branch
1658
974.1.81 by Aaron Bentley
Added ancestor revision namepsace
1659
def _trim_namespace(namespace, spec):
1660
    full_namespace = namespace + ':'
1661
    assert spec.startswith(full_namespace)
1662
    return spec[len(full_namespace):]