/brz/remove-bazaar

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