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