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