/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,
1293 by Martin Pool
- add Branch constructor option to relax version check
29
                           NoSuchRevision, HistoryMissing, NotBranchError)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
30
from bzrlib.textui import show_status
1263 by Martin Pool
- clean up imports
31
from bzrlib.revision import Revision, validate_revision_id
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
32
from bzrlib.delta import compare_trees
33
from bzrlib.tree import EmptyTree, RevisionTree
1192 by Martin Pool
- clean up code for retrieving stored inventories
34
from bzrlib.inventory import Inventory
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
35
from bzrlib.weavestore import WeaveStore
1225 by Martin Pool
- branch now tracks ancestry - all merged revisions
36
from bzrlib.store import ImmutableStore
1189 by Martin Pool
- BROKEN: partial support for commit into weave
37
import bzrlib.xml5
1104 by Martin Pool
- Add a simple UIFactory
38
import bzrlib.ui
39
1094 by Martin Pool
- merge aaron's merge improvements 999..1008
40
1223 by Martin Pool
- store inventories in weave
41
INVENTORY_FILEID = '__inventory'
42
ANCESTRY_FILEID = '__ancestry'
43
1110 by Martin Pool
- merge aaron's merge improvements:
44
1186 by Martin Pool
- start implementing v5 format; Branch refuses to operate on old branches
45
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
46
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
1 by mbp at sourcefrog
import from baz patch-364
47
## TODO: Maybe include checks for common corruption of newlines, etc?
48
49
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
50
# TODO: Some operations like log might retrieve the same revisions
51
# repeatedly to calculate deltas.  We could perhaps have a weakref
1223 by Martin Pool
- store inventories in weave
52
# cache in memory to make this faster.  In general anything can be
53
# cached in memory between lock and unlock operations.
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
54
55
# TODO: please move the revision-string syntax stuff out of the branch
56
# object; it's clutter
57
1 by mbp at sourcefrog
import from baz patch-364
58
416 by Martin Pool
- bzr log and bzr root now accept an http URL
59
def find_branch(f, **args):
455 by Martin Pool
- fix 'bzr root'
60
    if f and (f.startswith('http://') or f.startswith('https://')):
416 by Martin Pool
- bzr log and bzr root now accept an http URL
61
        import remotebranch 
62
        return remotebranch.RemoteBranch(f, **args)
63
    else:
64
        return Branch(f, **args)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
65
66
790 by Martin Pool
Merge from aaron:
67
def find_cached_branch(f, cache_root, **args):
68
    from remotebranch import RemoteBranch
69
    br = find_branch(f, **args)
70
    def cacheify(br, store_name):
71
        from meta_store import CachedStore
72
        cache_path = os.path.join(cache_root, store_name)
73
        os.mkdir(cache_path)
74
        new_store = CachedStore(getattr(br, store_name), cache_path)
75
        setattr(br, store_name, new_store)
76
77
    if isinstance(br, RemoteBranch):
78
        cacheify(br, 'inventory_store')
79
        cacheify(br, 'text_store')
80
        cacheify(br, 'revision_store')
81
    return br
82
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
83
600 by Martin Pool
- Better Branch.relpath that doesn't match on
84
def _relpath(base, path):
85
    """Return path relative to base, or raise exception.
86
87
    The path may be either an absolute path or a path relative to the
88
    current working directory.
89
90
    Lifted out of Branch.relpath for ease of testing.
91
92
    os.path.commonprefix (python2.4) has a bad bug that it works just
93
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
94
    avoids that problem."""
95
    rp = os.path.abspath(path)
96
97
    s = []
98
    head = rp
99
    while len(head) >= len(base):
100
        if head == base:
101
            break
102
        head, tail = os.path.split(head)
103
        if tail:
104
            s.insert(0, tail)
105
    else:
106
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
107
108
    return os.sep.join(s)
416 by Martin Pool
- bzr log and bzr root now accept an http URL
109
        
110
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
111
def find_branch_root(f=None):
112
    """Find the branch root enclosing f, or pwd.
113
416 by Martin Pool
- bzr log and bzr root now accept an http URL
114
    f may be a filename or a URL.
115
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
116
    It is not necessary that f exists.
117
118
    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
119
    run into the root.  If there isn't one, raises NotBranchError.
120
    """
184 by mbp at sourcefrog
pychecker fixups
121
    if f == None:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
122
        f = os.getcwd()
123
    elif hasattr(os.path, 'realpath'):
124
        f = os.path.realpath(f)
125
    else:
126
        f = os.path.abspath(f)
425 by Martin Pool
- check from aaron for existence of a branch
127
    if not os.path.exists(f):
128
        raise BzrError('%r does not exist' % f)
129
        
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
130
131
    orig_f = f
132
133
    while True:
134
        if os.path.exists(os.path.join(f, bzrlib.BZRDIR)):
135
            return f
136
        head, tail = os.path.split(f)
137
        if head == f:
138
            # reached the root, whatever that may be
1293 by Martin Pool
- add Branch constructor option to relax version check
139
            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
140
        f = head
1074 by Martin Pool
- check for email address in BRANCH_ROOT/.bzr/email, so you can
141
142
143
144
# XXX: move into bzrlib.errors; subclass BzrError    
628 by Martin Pool
- merge aaron's updated merge/pull code
145
class DivergedBranches(Exception):
146
    def __init__(self, branch1, branch2):
147
        self.branch1 = branch1
148
        self.branch2 = branch2
149
        Exception.__init__(self, "These branches have diverged.")
1 by mbp at sourcefrog
import from baz patch-364
150
685 by Martin Pool
- add -r option to the branch command
151
1 by mbp at sourcefrog
import from baz patch-364
152
######################################################################
153
# branch objects
154
558 by Martin Pool
- All top-level classes inherit from object
155
class Branch(object):
1 by mbp at sourcefrog
import from baz patch-364
156
    """Branch holding a history of revisions.
157
343 by Martin Pool
doc
158
    base
159
        Base directory of the branch.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
160
161
    _lock_mode
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
162
        None, or 'r' or 'w'
163
164
    _lock_count
165
        If _lock_mode is true, a positive count of the number of times the
166
        lock has been taken.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
167
614 by Martin Pool
- unify two defintions of LockError
168
    _lock
169
        Lock object from bzrlib.lock.
1 by mbp at sourcefrog
import from baz patch-364
170
    """
564 by Martin Pool
- Set Branch.base in class def to avoid it being undefined
171
    base = None
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
172
    _lock_mode = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
173
    _lock_count = None
615 by Martin Pool
Major rework of locking code:
174
    _lock = None
1223 by Martin Pool
- store inventories in weave
175
    _inventory_weave = None
353 by Martin Pool
- Per-branch locks in read and write modes.
176
    
897 by Martin Pool
- merge john's revision-naming code
177
    # Map some sort of prefix into a namespace
178
    # stuff like "revno:10", "revid:", etc.
179
    # This should match a prefix with a function which accepts
180
    REVISION_NAMESPACES = {}
181
1293 by Martin Pool
- add Branch constructor option to relax version check
182
    def __init__(self, base, init=False, find_root=True,
183
                 relax_version_check=False):
1 by mbp at sourcefrog
import from baz patch-364
184
        """Create new branch object at a particular location.
185
254 by Martin Pool
- Doc cleanups from Magnus Therning
186
        base -- Base directory for the branch.
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
187
        
254 by Martin Pool
- Doc cleanups from Magnus Therning
188
        init -- If True, create new control files in a previously
1 by mbp at sourcefrog
import from baz patch-364
189
             unversioned directory.  If False, the branch must already
190
             be versioned.
191
254 by Martin Pool
- Doc cleanups from Magnus Therning
192
        find_root -- If true and init is false, find the root of the
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
193
             existing branch containing base.
194
1293 by Martin Pool
- add Branch constructor option to relax version check
195
        relax_version_check -- If true, the usual check for the branch
196
            version is not applied.  This is intended only for
197
            upgrade/recovery type use; it's not guaranteed that
198
            all operations will work on old format branches.
199
1 by mbp at sourcefrog
import from baz patch-364
200
        In the test suite, creation of new trees is tested using the
201
        `ScratchBranch` class.
202
        """
203
        if init:
64 by mbp at sourcefrog
- fix up init command for new find-branch-root function
204
            self.base = os.path.realpath(base)
1 by mbp at sourcefrog
import from baz patch-364
205
            self._make_control()
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
206
        elif find_root:
207
            self.base = find_branch_root(base)
1 by mbp at sourcefrog
import from baz patch-364
208
        else:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
209
            self.base = os.path.realpath(base)
1 by mbp at sourcefrog
import from baz patch-364
210
            if not isdir(self.controlfilename('.')):
576 by Martin Pool
- raise exceptions rather than using bailout()
211
                raise NotBranchError("not a bzr branch: %s" % quotefn(base),
1293 by Martin Pool
- add Branch constructor option to relax version check
212
                                     ['use "bzr init" to initialize a new working tree'])
213
        
214
        self._check_format(relax_version_check)
1 by mbp at sourcefrog
import from baz patch-364
215
1196 by Martin Pool
- [WIP] retrieve historical texts from weaves
216
        self.weave_store = WeaveStore(self.controlfilename('weaves'))
1 by mbp at sourcefrog
import from baz patch-364
217
        self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
218
219
220
    def __str__(self):
221
        return '%s(%r)' % (self.__class__.__name__, self.base)
222
223
224
    __repr__ = __str__
225
226
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
227
    def __del__(self):
615 by Martin Pool
Major rework of locking code:
228
        if self._lock_mode or self._lock:
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
229
            from warnings import warn
230
            warn("branch %r was not explicitly unlocked" % self)
615 by Martin Pool
Major rework of locking code:
231
            self._lock.unlock()
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
232
233
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
234
    def lock_write(self):
235
        if self._lock_mode:
236
            if self._lock_mode != 'w':
237
                from errors import LockError
238
                raise LockError("can't upgrade to a write lock from %r" %
239
                                self._lock_mode)
240
            self._lock_count += 1
241
        else:
615 by Martin Pool
Major rework of locking code:
242
            from bzrlib.lock import WriteLock
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
243
615 by Martin Pool
Major rework of locking code:
244
            self._lock = WriteLock(self.controlfilename('branch-lock'))
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
245
            self._lock_mode = 'w'
246
            self._lock_count = 1
247
248
249
    def lock_read(self):
250
        if self._lock_mode:
251
            assert self._lock_mode in ('r', 'w'), \
252
                   "invalid lock mode %r" % self._lock_mode
253
            self._lock_count += 1
254
        else:
615 by Martin Pool
Major rework of locking code:
255
            from bzrlib.lock import ReadLock
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
256
615 by Martin Pool
Major rework of locking code:
257
            self._lock = ReadLock(self.controlfilename('branch-lock'))
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
258
            self._lock_mode = 'r'
259
            self._lock_count = 1
260
                        
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
261
    def unlock(self):
262
        if not self._lock_mode:
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
263
            from errors import LockError
264
            raise LockError('branch %r is not locked' % (self))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
265
266
        if self._lock_count > 1:
267
            self._lock_count -= 1
268
        else:
615 by Martin Pool
Major rework of locking code:
269
            self._lock.unlock()
270
            self._lock = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
271
            self._lock_mode = self._lock_count = None
353 by Martin Pool
- Per-branch locks in read and write modes.
272
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
273
    def abspath(self, name):
274
        """Return absolute filename for something in the branch"""
1 by mbp at sourcefrog
import from baz patch-364
275
        return os.path.join(self.base, name)
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
276
68 by mbp at sourcefrog
- new relpath command and function
277
    def relpath(self, path):
278
        """Return path relative to this branch of something inside it.
279
280
        Raises an error if path is not in this branch."""
600 by Martin Pool
- Better Branch.relpath that doesn't match on
281
        return _relpath(self.base, path)
68 by mbp at sourcefrog
- new relpath command and function
282
1 by mbp at sourcefrog
import from baz patch-364
283
    def controlfilename(self, file_or_path):
284
        """Return location relative to branch."""
800 by Martin Pool
Merge John's import-speedup branch:
285
        if isinstance(file_or_path, basestring):
1 by mbp at sourcefrog
import from baz patch-364
286
            file_or_path = [file_or_path]
287
        return os.path.join(self.base, bzrlib.BZRDIR, *file_or_path)
288
289
290
    def controlfile(self, file_or_path, mode='r'):
245 by mbp at sourcefrog
- control files always in utf-8-unix format
291
        """Open a control file for this branch.
292
293
        There are two classes of file in the control directory: text
294
        and binary.  binary files are untranslated byte streams.  Text
295
        control files are stored with Unix newlines and in UTF-8, even
296
        if the platform or locale defaults are different.
430 by Martin Pool
doc
297
298
        Controlfiles should almost never be opened in write mode but
299
        rather should be atomically copied and replaced using atomicfile.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
300
        """
301
302
        fn = self.controlfilename(file_or_path)
303
304
        if mode == 'rb' or mode == 'wb':
305
            return file(fn, mode)
306
        elif mode == 'r' or mode == 'w':
259 by Martin Pool
- use larger file buffers when opening branch control file
307
            # open in binary mode anyhow so there's no newline translation;
308
            # codecs uses line buffering by default; don't want that.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
309
            import codecs
259 by Martin Pool
- use larger file buffers when opening branch control file
310
            return codecs.open(fn, mode + 'b', 'utf-8',
311
                               buffering=60000)
245 by mbp at sourcefrog
- control files always in utf-8-unix format
312
        else:
313
            raise BzrError("invalid controlfile mode %r" % mode)
314
1 by mbp at sourcefrog
import from baz patch-364
315
    def _make_control(self):
316
        os.mkdir(self.controlfilename([]))
317
        self.controlfile('README', 'w').write(
318
            "This is a Bazaar-NG control directory.\n"
679 by Martin Pool
- put trailing newline on newly-created .bzr/README
319
            "Do not change any files in this directory.\n")
1186 by Martin Pool
- start implementing v5 format; Branch refuses to operate on old branches
320
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT_5)
1223 by Martin Pool
- store inventories in weave
321
        for d in ('text-store', 'revision-store',
1189 by Martin Pool
- BROKEN: partial support for commit into weave
322
                  'weaves'):
1 by mbp at sourcefrog
import from baz patch-364
323
            os.mkdir(self.controlfilename(d))
324
        for f in ('revision-history', 'merged-patches',
353 by Martin Pool
- Per-branch locks in read and write modes.
325
                  'pending-merged-patches', 'branch-name',
815 by Martin Pool
- track pending-merges
326
                  'branch-lock',
327
                  'pending-merges'):
1 by mbp at sourcefrog
import from baz patch-364
328
            self.controlfile(f, 'w').write('')
329
        mutter('created control directory in ' + self.base)
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
330
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
331
        # if we want per-tree root ids then this is the place to set
332
        # them; they're not needed for now and so ommitted for
333
        # simplicity.
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
334
        f = self.controlfile('inventory','w')
1189 by Martin Pool
- BROKEN: partial support for commit into weave
335
        bzrlib.xml5.serializer_v5.write_inventory(Inventory(), f)
1225 by Martin Pool
- branch now tracks ancestry - all merged revisions
336
        
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
337
1 by mbp at sourcefrog
import from baz patch-364
338
1293 by Martin Pool
- add Branch constructor option to relax version check
339
    def _check_format(self, relax_version_check):
1 by mbp at sourcefrog
import from baz patch-364
340
        """Check this branch format is supported.
341
1187 by Martin Pool
- improved check for branch version
342
        The format level is stored, as an integer, in
343
        self._branch_format for code that needs to check it later.
1 by mbp at sourcefrog
import from baz patch-364
344
345
        In the future, we might need different in-memory Branch
346
        classes to support downlevel branches.  But not yet.
163 by mbp at sourcefrog
merge win32 portability fixes
347
        """
1187 by Martin Pool
- improved check for branch version
348
        fmt = self.controlfile('branch-format', 'r').read()
349
        if fmt == BZR_BRANCH_FORMAT_5:
350
            self._branch_format = 5
1293 by Martin Pool
- add Branch constructor option to relax version check
351
            return
352
        elif relax_version_check:
353
            if fmt == BZR_BRANCH_FORMAT_4:
354
                self._branch_format = 4
355
                return
356
            
357
        raise BzrError('sorry, branch format "%s" not supported; ' 
358
                       'use a different bzr version, '
359
                       'or run "bzr upgrade"'
360
                       % fmt.rstrip('\n\r'))
361
        
1 by mbp at sourcefrog
import from baz patch-364
362
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
363
    def get_root_id(self):
364
        """Return the id of this branches root"""
365
        inv = self.read_working_inventory()
366
        return inv.root.file_id
1 by mbp at sourcefrog
import from baz patch-364
367
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
368
    def set_root_id(self, file_id):
369
        inv = self.read_working_inventory()
370
        orig_root_id = inv.root.file_id
371
        del inv._byid[inv.root.file_id]
372
        inv.root.file_id = file_id
373
        inv._byid[inv.root.file_id] = inv.root
374
        for fid in inv:
375
            entry = inv[fid]
376
            if entry.parent_id in (None, orig_root_id):
377
                entry.parent_id = inv.root.file_id
378
        self._write_inventory(inv)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
379
1 by mbp at sourcefrog
import from baz patch-364
380
    def read_working_inventory(self):
381
        """Read the working inventory."""
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
382
        self.lock_read()
383
        try:
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
384
            # ElementTree does its own conversion from UTF-8, so open in
385
            # binary.
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
386
            f = self.controlfile('inventory', 'rb')
1189 by Martin Pool
- BROKEN: partial support for commit into weave
387
            return bzrlib.xml5.serializer_v5.read_inventory(f)
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
388
        finally:
389
            self.unlock()
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
390
            
1 by mbp at sourcefrog
import from baz patch-364
391
392
    def _write_inventory(self, inv):
393
        """Update the working inventory.
394
395
        That is to say, the inventory describing changes underway, that
396
        will be committed to the next revision.
397
        """
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
398
        from bzrlib.atomicfile import AtomicFile
399
        
770 by Martin Pool
- write new working inventory using AtomicFile
400
        self.lock_write()
401
        try:
402
            f = AtomicFile(self.controlfilename('inventory'), 'wb')
403
            try:
1189 by Martin Pool
- BROKEN: partial support for commit into weave
404
                bzrlib.xml5.serializer_v5.write_inventory(inv, f)
770 by Martin Pool
- write new working inventory using AtomicFile
405
                f.commit()
406
            finally:
407
                f.close()
408
        finally:
409
            self.unlock()
410
        
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
411
        mutter('wrote working inventory')
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
412
            
1 by mbp at sourcefrog
import from baz patch-364
413
414
    inventory = property(read_working_inventory, _write_inventory, None,
415
                         """Inventory for the working copy.""")
416
417
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
418
    def add(self, files, ids=None):
1 by mbp at sourcefrog
import from baz patch-364
419
        """Make files versioned.
420
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
421
        Note that the command line normally calls smart_add instead,
422
        which can automatically recurse.
247 by mbp at sourcefrog
doc
423
1 by mbp at sourcefrog
import from baz patch-364
424
        This puts the files in the Added state, so that they will be
425
        recorded by the next commit.
426
596 by Martin Pool
doc
427
        files
428
            List of paths to add, relative to the base of the tree.
429
430
        ids
431
            If set, use these instead of automatically generated ids.
432
            Must be the same length as the list of files, but may
433
            contain None for ids that are to be autogenerated.
434
254 by Martin Pool
- Doc cleanups from Magnus Therning
435
        TODO: Perhaps have an option to add the ids even if the files do
596 by Martin Pool
doc
436
              not (yet) exist.
1 by mbp at sourcefrog
import from baz patch-364
437
1129 by Martin Pool
- Branch.add shouldn't write to stdout either
438
        TODO: Perhaps yield the ids and paths as they're added.
1 by mbp at sourcefrog
import from baz patch-364
439
        """
440
        # TODO: Re-adding a file that is removed in the working copy
441
        # should probably put it back with the previous ID.
800 by Martin Pool
Merge John's import-speedup branch:
442
        if isinstance(files, basestring):
443
            assert(ids is None or isinstance(ids, basestring))
1 by mbp at sourcefrog
import from baz patch-364
444
            files = [files]
493 by Martin Pool
- Merge aaron's merge command
445
            if ids is not None:
446
                ids = [ids]
447
448
        if ids is None:
449
            ids = [None] * len(files)
450
        else:
451
            assert(len(ids) == len(files))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
452
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
453
        self.lock_write()
454
        try:
455
            inv = self.read_working_inventory()
456
            for f,file_id in zip(files, ids):
457
                if is_control_file(f):
458
                    raise BzrError("cannot add control file %s" % quotefn(f))
459
460
                fp = splitpath(f)
461
462
                if len(fp) == 0:
463
                    raise BzrError("cannot add top-level %r" % f)
464
465
                fullpath = os.path.normpath(self.abspath(f))
466
467
                try:
468
                    kind = file_kind(fullpath)
469
                except OSError:
470
                    # maybe something better?
471
                    raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
472
473
                if kind != 'file' and kind != 'directory':
474
                    raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
475
476
                if file_id is None:
477
                    file_id = gen_file_id(f)
478
                inv.add_path(f, kind=kind, file_id=file_id)
479
480
                mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
481
482
            self._write_inventory(inv)
483
        finally:
484
            self.unlock()
70 by mbp at sourcefrog
Prepare for smart recursive add.
485
            
1 by mbp at sourcefrog
import from baz patch-364
486
176 by mbp at sourcefrog
New cat command contributed by janmar.
487
    def print_file(self, file, revno):
488
        """Print `file` to stdout."""
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
489
        self.lock_read()
490
        try:
491
            tree = self.revision_tree(self.lookup_revision(revno))
492
            # use inventory as it was in that revision
493
            file_id = tree.inventory.path2id(file)
494
            if not file_id:
897 by Martin Pool
- merge john's revision-naming code
495
                raise BzrError("%r is not present in revision %s" % (file, revno))
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
496
            tree.print_file(file_id)
497
        finally:
498
            self.unlock()
499
500
1 by mbp at sourcefrog
import from baz patch-364
501
    def remove(self, files, verbose=False):
502
        """Mark nominated files for removal from the inventory.
503
504
        This does not remove their text.  This does not run on 
505
254 by Martin Pool
- Doc cleanups from Magnus Therning
506
        TODO: Refuse to remove modified files unless --force is given?
1 by mbp at sourcefrog
import from baz patch-364
507
254 by Martin Pool
- Doc cleanups from Magnus Therning
508
        TODO: Do something useful with directories.
1 by mbp at sourcefrog
import from baz patch-364
509
254 by Martin Pool
- Doc cleanups from Magnus Therning
510
        TODO: Should this remove the text or not?  Tough call; not
1 by mbp at sourcefrog
import from baz patch-364
511
        removing may be useful and the user can just use use rm, and
512
        is the opposite of add.  Removing it is consistent with most
513
        other tools.  Maybe an option.
514
        """
515
        ## TODO: Normalize names
516
        ## TODO: Remove nested loops; better scalability
800 by Martin Pool
Merge John's import-speedup branch:
517
        if isinstance(files, basestring):
1 by mbp at sourcefrog
import from baz patch-364
518
            files = [files]
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
519
611 by Martin Pool
- remove @with_writelock, @with_readlock decorators
520
        self.lock_write()
521
522
        try:
523
            tree = self.working_tree()
524
            inv = tree.inventory
525
526
            # do this before any modifications
527
            for f in files:
528
                fid = inv.path2id(f)
529
                if not fid:
530
                    raise BzrError("cannot remove unversioned file %s" % quotefn(f))
531
                mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
532
                if verbose:
533
                    # having remove it, it must be either ignored or unknown
534
                    if tree.is_ignored(f):
535
                        new_status = 'I'
536
                    else:
537
                        new_status = '?'
538
                    show_status(new_status, inv[fid].kind, quotefn(f))
539
                del inv[fid]
540
541
            self._write_inventory(inv)
542
        finally:
543
            self.unlock()
544
545
612 by Martin Pool
doc
546
    # FIXME: this doesn't need to be a branch method
493 by Martin Pool
- Merge aaron's merge command
547
    def set_inventory(self, new_inventory_list):
800 by Martin Pool
Merge John's import-speedup branch:
548
        from bzrlib.inventory import Inventory, InventoryEntry
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
549
        inv = Inventory(self.get_root_id())
493 by Martin Pool
- Merge aaron's merge command
550
        for path, file_id, parent, kind in new_inventory_list:
551
            name = os.path.basename(path)
552
            if name == "":
553
                continue
554
            inv.add(InventoryEntry(file_id, name, kind, parent))
555
        self._write_inventory(inv)
556
1 by mbp at sourcefrog
import from baz patch-364
557
558
    def unknowns(self):
559
        """Return all unknown files.
560
561
        These are files in the working directory that are not versioned or
562
        control files or ignored.
563
        
564
        >>> b = ScratchBranch(files=['foo', 'foo~'])
565
        >>> list(b.unknowns())
566
        ['foo']
567
        >>> b.add('foo')
568
        >>> list(b.unknowns())
569
        []
570
        >>> b.remove('foo')
571
        >>> list(b.unknowns())
572
        ['foo']
573
        """
574
        return self.working_tree().unknowns()
575
576
905 by Martin Pool
- merge aaron's append_multiple.patch
577
    def append_revision(self, *revision_ids):
769 by Martin Pool
- append to branch revision history using AtomicFile
578
        from bzrlib.atomicfile import AtomicFile
579
905 by Martin Pool
- merge aaron's append_multiple.patch
580
        for revision_id in revision_ids:
581
            mutter("add {%s} to revision-history" % revision_id)
582
583
        rev_history = self.revision_history()
584
        rev_history.extend(revision_ids)
769 by Martin Pool
- append to branch revision history using AtomicFile
585
586
        f = AtomicFile(self.controlfilename('revision-history'))
587
        try:
588
            for rev_id in rev_history:
589
                print >>f, rev_id
590
            f.commit()
591
        finally:
592
            f.close()
233 by mbp at sourcefrog
- more output from test.sh
593
594
1261 by Martin Pool
- new method Branch.has_revision
595
    def has_revision(self, revision_id):
596
        """True if this branch has a copy of the revision.
597
598
        This does not necessarily imply the revision is merge
599
        or on the mainline."""
600
        return revision_id in self.revision_store
601
602
1182 by Martin Pool
- more disentangling of xml storage format from objects
603
    def get_revision_xml_file(self, revision_id):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
604
        """Return XML file object for revision object."""
605
        if not revision_id or not isinstance(revision_id, basestring):
606
            raise InvalidRevisionId(revision_id)
607
608
        self.lock_read()
609
        try:
610
            try:
611
                return self.revision_store[revision_id]
612
            except IndexError:
613
                raise bzrlib.errors.NoSuchRevision(self, revision_id)
614
        finally:
615
            self.unlock()
616
617
1231 by Martin Pool
- more progress on fetch on top of weaves
618
    def get_revision_xml(self, revision_id):
619
        return self.get_revision_xml_file(revision_id).read()
620
621
1 by mbp at sourcefrog
import from baz patch-364
622
    def get_revision(self, revision_id):
623
        """Return the Revision object for a named revision"""
1182 by Martin Pool
- more disentangling of xml storage format from objects
624
        xml_file = self.get_revision_xml_file(revision_id)
1027 by Martin Pool
- better error message when failing to get revision from store
625
626
        try:
1189 by Martin Pool
- BROKEN: partial support for commit into weave
627
            r = bzrlib.xml5.serializer_v5.read_revision(xml_file)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
628
        except SyntaxError, e:
629
            raise bzrlib.errors.BzrError('failed to unpack revision_xml',
630
                                         [revision_id,
631
                                          str(e)])
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
632
            
1 by mbp at sourcefrog
import from baz patch-364
633
        assert r.revision_id == revision_id
634
        return r
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
635
636
637
    def get_revision_delta(self, revno):
638
        """Return the delta for one revision.
639
640
        The delta is relative to its mainline predecessor, or the
641
        empty tree for revision 1.
642
        """
643
        assert isinstance(revno, int)
644
        rh = self.revision_history()
645
        if not (1 <= revno <= len(rh)):
646
            raise InvalidRevisionNumber(revno)
647
648
        # revno is 1-based; list is 0-based
649
650
        new_tree = self.revision_tree(rh[revno-1])
651
        if revno == 1:
652
            old_tree = EmptyTree()
653
        else:
654
            old_tree = self.revision_tree(rh[revno-2])
655
656
        return compare_trees(old_tree, new_tree)
657
802 by Martin Pool
- Remove XMLMixin class in favour of simple pack_xml, unpack_xml functions
658
        
1 by mbp at sourcefrog
import from baz patch-364
659
672 by Martin Pool
- revision records include the hash of their inventory and
660
    def get_revision_sha1(self, revision_id):
661
        """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
662
        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
663
1 by mbp at sourcefrog
import from baz patch-364
664
1225 by Martin Pool
- branch now tracks ancestry - all merged revisions
665
    def get_ancestry(self, revision_id):
666
        """Return a list of revision-ids integrated by a revision.
667
        """
668
        w = self.weave_store.get_weave(ANCESTRY_FILEID)
669
        # strip newlines
670
        return [l[:-1] for l in w.get_iter(w.lookup(revision_id))]
671
672
1223 by Martin Pool
- store inventories in weave
673
    def get_inventory_weave(self):
674
        return self.weave_store.get_weave(INVENTORY_FILEID)
675
676
1192 by Martin Pool
- clean up code for retrieving stored inventories
677
    def get_inventory(self, revision_id):
1223 by Martin Pool
- store inventories in weave
678
        """Get Inventory object by hash."""
679
        # FIXME: The text gets passed around a lot coming from the weave.
680
        f = StringIO(self.get_inventory_xml(revision_id))
1189 by Martin Pool
- BROKEN: partial support for commit into weave
681
        return bzrlib.xml5.serializer_v5.read_inventory(f)
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
682
683
1192 by Martin Pool
- clean up code for retrieving stored inventories
684
    def get_inventory_xml(self, revision_id):
974.1.26 by aaron.bentley at utoronto
merged mbp@sourcefrog.net-20050817233101-0939da1cf91f2472
685
        """Get inventory XML as a file object."""
1192 by Martin Pool
- clean up code for retrieving stored inventories
686
        try:
687
            assert isinstance(revision_id, basestring), type(revision_id)
1223 by Martin Pool
- store inventories in weave
688
            iw = self.get_inventory_weave()
689
            return iw.get_text(iw.lookup(revision_id))
1192 by Martin Pool
- clean up code for retrieving stored inventories
690
        except IndexError:
691
            raise bzrlib.errors.HistoryMissing(self, 'inventory', revision_id)
1180 by Martin Pool
- start splitting code for xml (de)serialization away from objects
692
1 by mbp at sourcefrog
import from baz patch-364
693
1192 by Martin Pool
- clean up code for retrieving stored inventories
694
    def get_inventory_sha1(self, revision_id):
672 by Martin Pool
- revision records include the hash of their inventory and
695
        """Return the sha1 hash of the inventory entry
696
        """
1223 by Martin Pool
- store inventories in weave
697
        return self.get_revision(revision_id).inventory_sha1
672 by Martin Pool
- revision records include the hash of their inventory and
698
1 by mbp at sourcefrog
import from baz patch-364
699
700
    def get_revision_inventory(self, revision_id):
701
        """Return inventory of a past revision."""
1218 by Martin Pool
- fix up import
702
        # 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
703
        # must be the same as its revision, so this is trivial.
1 by mbp at sourcefrog
import from baz patch-364
704
        if revision_id == None:
909 by Martin Pool
- merge John's code to give the tree root an explicit file id
705
            return Inventory(self.get_root_id())
1 by mbp at sourcefrog
import from baz patch-364
706
        else:
820 by Martin Pool
- faster Branch.get_revision_inventory now we know the ids are the same
707
            return self.get_inventory(revision_id)
1 by mbp at sourcefrog
import from baz patch-364
708
709
710
    def revision_history(self):
711
        """Return sequence of revision hashes on to this branch.
712
713
        >>> ScratchBranch().revision_history()
714
        []
715
        """
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