/brz/remove-bazaar

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