/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
18
import sys, os, os.path, random, time, sha, sets, types, re, shutil, tempfile
19
import traceback, socket, fnmatch, difflib, time
20
from binascii import hexlify
21
22
import bzrlib
23
from inventory import Inventory
24
from trace import mutter, note
453 by Martin Pool
- Split WorkingTree into its own file
25
from tree import Tree, EmptyTree, RevisionTree
1 by mbp at sourcefrog
import from baz patch-364
26
from inventory import InventoryEntry, Inventory
319 by Martin Pool
- remove trivial chomp() function
27
from osutils import isdir, quotefn, isfile, uuid, sha_file, username, \
1 by mbp at sourcefrog
import from baz patch-364
28
     format_date, compact_date, pumpfile, user_email, rand_bytes, splitpath, \
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
29
     joinpath, sha_string, file_kind, local_time_offset, appendpath
1 by mbp at sourcefrog
import from baz patch-364
30
from store import ImmutableStore
31
from revision import Revision
576 by Martin Pool
- raise exceptions rather than using bailout()
32
from errors import BzrError
1 by mbp at sourcefrog
import from baz patch-364
33
from textui import show_status
34
35
BZR_BRANCH_FORMAT = "Bazaar-NG branch, format 0.0.4\n"
36
## TODO: Maybe include checks for common corruption of newlines, etc?
37
38
39
416 by Martin Pool
- bzr log and bzr root now accept an http URL
40
def find_branch(f, **args):
455 by Martin Pool
- fix 'bzr root'
41
    if f and (f.startswith('http://') or f.startswith('https://')):
416 by Martin Pool
- bzr log and bzr root now accept an http URL
42
        import remotebranch 
43
        return remotebranch.RemoteBranch(f, **args)
44
    else:
45
        return Branch(f, **args)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
46
47
48
49
def with_writelock(method):
50
    """Method decorator for functions run with the branch locked."""
51
    def d(self, *a, **k):
52
        # called with self set to the branch
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
53
        self.lock_write()
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
54
        try:
55
            return method(self, *a, **k)
56
        finally:
57
            self.unlock()
58
    return d
59
60
61
def with_readlock(method):
62
    def d(self, *a, **k):
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
63
        self.lock_read()
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
64
        try:
65
            return method(self, *a, **k)
66
        finally:
67
            self.unlock()
68
    return d
600 by Martin Pool
- Better Branch.relpath that doesn't match on
69
70
71
def _relpath(base, path):
72
    """Return path relative to base, or raise exception.
73
74
    The path may be either an absolute path or a path relative to the
75
    current working directory.
76
77
    Lifted out of Branch.relpath for ease of testing.
78
79
    os.path.commonprefix (python2.4) has a bad bug that it works just
80
    on string prefixes, assuming that '/u' is a prefix of '/u2'.  This
81
    avoids that problem."""
82
    rp = os.path.abspath(path)
83
84
    s = []
85
    head = rp
86
    while len(head) >= len(base):
87
        if head == base:
88
            break
89
        head, tail = os.path.split(head)
90
        if tail:
91
            s.insert(0, tail)
92
    else:
93
        from errors import NotBranchError
94
        raise NotBranchError("path %r is not within branch %r" % (rp, base))
95
96
    return os.sep.join(s)
416 by Martin Pool
- bzr log and bzr root now accept an http URL
97
        
98
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
99
def find_branch_root(f=None):
100
    """Find the branch root enclosing f, or pwd.
101
416 by Martin Pool
- bzr log and bzr root now accept an http URL
102
    f may be a filename or a URL.
103
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
104
    It is not necessary that f exists.
105
106
    Basically we keep looking up until we find the control directory or
107
    run into the root."""
184 by mbp at sourcefrog
pychecker fixups
108
    if f == None:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
109
        f = os.getcwd()
110
    elif hasattr(os.path, 'realpath'):
111
        f = os.path.realpath(f)
112
    else:
113
        f = os.path.abspath(f)
425 by Martin Pool
- check from aaron for existence of a branch
114
    if not os.path.exists(f):
115
        raise BzrError('%r does not exist' % f)
116
        
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
117
118
    orig_f = f
119
120
    while True:
121
        if os.path.exists(os.path.join(f, bzrlib.BZRDIR)):
122
            return f
123
        head, tail = os.path.split(f)
124
        if head == f:
125
            # reached the root, whatever that may be
184 by mbp at sourcefrog
pychecker fixups
126
            raise BzrError('%r is not in a branch' % orig_f)
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
127
        f = head
128
    
1 by mbp at sourcefrog
import from baz patch-364
129
130
131
######################################################################
132
# branch objects
133
558 by Martin Pool
- All top-level classes inherit from object
134
class Branch(object):
1 by mbp at sourcefrog
import from baz patch-364
135
    """Branch holding a history of revisions.
136
343 by Martin Pool
doc
137
    base
138
        Base directory of the branch.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
139
140
    _lock_mode
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
141
        None, or 'r' or 'w'
142
143
    _lock_count
144
        If _lock_mode is true, a positive count of the number of times the
145
        lock has been taken.
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
146
147
    _lockfile
148
        Open file used for locking.
1 by mbp at sourcefrog
import from baz patch-364
149
    """
564 by Martin Pool
- Set Branch.base in class def to avoid it being undefined
150
    base = None
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
151
    _lock_mode = None
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
152
    _lock_count = None
353 by Martin Pool
- Per-branch locks in read and write modes.
153
    
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
154
    def __init__(self, base, init=False, find_root=True):
1 by mbp at sourcefrog
import from baz patch-364
155
        """Create new branch object at a particular location.
156
254 by Martin Pool
- Doc cleanups from Magnus Therning
157
        base -- Base directory for the branch.
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
158
        
254 by Martin Pool
- Doc cleanups from Magnus Therning
159
        init -- If True, create new control files in a previously
1 by mbp at sourcefrog
import from baz patch-364
160
             unversioned directory.  If False, the branch must already
161
             be versioned.
162
254 by Martin Pool
- Doc cleanups from Magnus Therning
163
        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
164
             existing branch containing base.
165
1 by mbp at sourcefrog
import from baz patch-364
166
        In the test suite, creation of new trees is tested using the
167
        `ScratchBranch` class.
168
        """
169
        if init:
64 by mbp at sourcefrog
- fix up init command for new find-branch-root function
170
            self.base = os.path.realpath(base)
1 by mbp at sourcefrog
import from baz patch-364
171
            self._make_control()
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
172
        elif find_root:
173
            self.base = find_branch_root(base)
1 by mbp at sourcefrog
import from baz patch-364
174
        else:
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
175
            self.base = os.path.realpath(base)
1 by mbp at sourcefrog
import from baz patch-364
176
            if not isdir(self.controlfilename('.')):
576 by Martin Pool
- raise exceptions rather than using bailout()
177
                from errors import NotBranchError
178
                raise NotBranchError("not a bzr branch: %s" % quotefn(base),
179
                                     ['use "bzr init" to initialize a new working tree',
180
                                      'current bzr can only operate from top-of-tree'])
62 by mbp at sourcefrog
- new find_branch_root function; based on suggestion from aaron
181
        self._check_format()
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
182
        self._lockfile = self.controlfile('branch-lock', 'wb')
1 by mbp at sourcefrog
import from baz patch-364
183
184
        self.text_store = ImmutableStore(self.controlfilename('text-store'))
185
        self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
186
        self.inventory_store = ImmutableStore(self.controlfilename('inventory-store'))
187
188
189
    def __str__(self):
190
        return '%s(%r)' % (self.__class__.__name__, self.base)
191
192
193
    __repr__ = __str__
194
195
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
196
    def __del__(self):
197
        if self._lock_mode:
198
            from warnings import warn
199
            warn("branch %r was not explicitly unlocked" % self)
200
            self.unlock()
201
202
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
203
204
    def lock_write(self):
205
        if self._lock_mode:
206
            if self._lock_mode != 'w':
207
                from errors import LockError
208
                raise LockError("can't upgrade to a write lock from %r" %
209
                                self._lock_mode)
210
            self._lock_count += 1
211
        else:
212
            from bzrlib.lock import lock, LOCK_EX
213
214
            lock(self._lockfile, LOCK_EX)
215
            self._lock_mode = 'w'
216
            self._lock_count = 1
217
218
219
220
    def lock_read(self):
221
        if self._lock_mode:
222
            assert self._lock_mode in ('r', 'w'), \
223
                   "invalid lock mode %r" % self._lock_mode
224
            self._lock_count += 1
225
        else:
226
            from bzrlib.lock import lock, LOCK_SH
227
228
            lock(self._lockfile, LOCK_SH)
229
            self._lock_mode = 'r'
230
            self._lock_count = 1
231
                        
232
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
233
            
578 by Martin Pool
- start to move toward Branch.lock and unlock methods,
234
    def unlock(self):
235
        if not self._lock_mode:
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
236
            from errors import LockError
237
            raise LockError('branch %r is not locked' % (self))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
238
239
        if self._lock_count > 1:
240
            self._lock_count -= 1
241
        else:
242
            assert self._lock_count == 1
243
            from bzrlib.lock import unlock
244
            unlock(self._lockfile)
245
            self._lock_mode = self._lock_count = None
353 by Martin Pool
- Per-branch locks in read and write modes.
246
247
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
248
    def abspath(self, name):
249
        """Return absolute filename for something in the branch"""
1 by mbp at sourcefrog
import from baz patch-364
250
        return os.path.join(self.base, name)
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
251
1 by mbp at sourcefrog
import from baz patch-364
252
68 by mbp at sourcefrog
- new relpath command and function
253
    def relpath(self, path):
254
        """Return path relative to this branch of something inside it.
255
256
        Raises an error if path is not in this branch."""
600 by Martin Pool
- Better Branch.relpath that doesn't match on
257
        return _relpath(self.base, path)
68 by mbp at sourcefrog
- new relpath command and function
258
259
1 by mbp at sourcefrog
import from baz patch-364
260
    def controlfilename(self, file_or_path):
261
        """Return location relative to branch."""
262
        if isinstance(file_or_path, types.StringTypes):
263
            file_or_path = [file_or_path]
264
        return os.path.join(self.base, bzrlib.BZRDIR, *file_or_path)
265
266
267
    def controlfile(self, file_or_path, mode='r'):
245 by mbp at sourcefrog
- control files always in utf-8-unix format
268
        """Open a control file for this branch.
269
270
        There are two classes of file in the control directory: text
271
        and binary.  binary files are untranslated byte streams.  Text
272
        control files are stored with Unix newlines and in UTF-8, even
273
        if the platform or locale defaults are different.
430 by Martin Pool
doc
274
275
        Controlfiles should almost never be opened in write mode but
276
        rather should be atomically copied and replaced using atomicfile.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
277
        """
278
279
        fn = self.controlfilename(file_or_path)
280
281
        if mode == 'rb' or mode == 'wb':
282
            return file(fn, mode)
283
        elif mode == 'r' or mode == 'w':
259 by Martin Pool
- use larger file buffers when opening branch control file
284
            # open in binary mode anyhow so there's no newline translation;
285
            # codecs uses line buffering by default; don't want that.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
286
            import codecs
259 by Martin Pool
- use larger file buffers when opening branch control file
287
            return codecs.open(fn, mode + 'b', 'utf-8',
288
                               buffering=60000)
245 by mbp at sourcefrog
- control files always in utf-8-unix format
289
        else:
290
            raise BzrError("invalid controlfile mode %r" % mode)
291
1 by mbp at sourcefrog
import from baz patch-364
292
293
294
    def _make_control(self):
295
        os.mkdir(self.controlfilename([]))
296
        self.controlfile('README', 'w').write(
297
            "This is a Bazaar-NG control directory.\n"
298
            "Do not change any files in this directory.")
245 by mbp at sourcefrog
- control files always in utf-8-unix format
299
        self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
1 by mbp at sourcefrog
import from baz patch-364
300
        for d in ('text-store', 'inventory-store', 'revision-store'):
301
            os.mkdir(self.controlfilename(d))
302
        for f in ('revision-history', 'merged-patches',
353 by Martin Pool
- Per-branch locks in read and write modes.
303
                  'pending-merged-patches', 'branch-name',
304
                  'branch-lock'):
1 by mbp at sourcefrog
import from baz patch-364
305
            self.controlfile(f, 'w').write('')
306
        mutter('created control directory in ' + self.base)
307
        Inventory().write_xml(self.controlfile('inventory','w'))
308
309
310
    def _check_format(self):
311
        """Check this branch format is supported.
312
313
        The current tool only supports the current unstable format.
314
315
        In the future, we might need different in-memory Branch
316
        classes to support downlevel branches.  But not yet.
163 by mbp at sourcefrog
merge win32 portability fixes
317
        """
318
        # This ignores newlines so that we can open branches created
319
        # on Windows from Linux and so on.  I think it might be better
320
        # to always make all internal files in unix format.
245 by mbp at sourcefrog
- control files always in utf-8-unix format
321
        fmt = self.controlfile('branch-format', 'r').read()
163 by mbp at sourcefrog
merge win32 portability fixes
322
        fmt.replace('\r\n', '')
1 by mbp at sourcefrog
import from baz patch-364
323
        if fmt != BZR_BRANCH_FORMAT:
576 by Martin Pool
- raise exceptions rather than using bailout()
324
            raise BzrError('sorry, branch format %r not supported' % fmt,
325
                           ['use a different bzr version',
326
                            'or remove the .bzr directory and "bzr init" again'])
1 by mbp at sourcefrog
import from baz patch-364
327
328
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
329
330
    @with_readlock
1 by mbp at sourcefrog
import from baz patch-364
331
    def read_working_inventory(self):
332
        """Read the working inventory."""
333
        before = time.time()
245 by mbp at sourcefrog
- control files always in utf-8-unix format
334
        # ElementTree does its own conversion from UTF-8, so open in
335
        # binary.
336
        inv = Inventory.read_xml(self.controlfile('inventory', 'rb'))
1 by mbp at sourcefrog
import from baz patch-364
337
        mutter("loaded inventory of %d items in %f"
338
               % (len(inv), time.time() - before))
339
        return inv
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
340
            
1 by mbp at sourcefrog
import from baz patch-364
341
342
    def _write_inventory(self, inv):
343
        """Update the working inventory.
344
345
        That is to say, the inventory describing changes underway, that
346
        will be committed to the next revision.
347
        """
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
348
        ## TODO: factor out to atomicfile?  is rename safe on windows?
70 by mbp at sourcefrog
Prepare for smart recursive add.
349
        ## TODO: Maybe some kind of clean/dirty marker on inventory?
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
350
        tmpfname = self.controlfilename('inventory.tmp')
245 by mbp at sourcefrog
- control files always in utf-8-unix format
351
        tmpf = file(tmpfname, 'wb')
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
352
        inv.write_xml(tmpf)
353
        tmpf.close()
163 by mbp at sourcefrog
merge win32 portability fixes
354
        inv_fname = self.controlfilename('inventory')
355
        if sys.platform == 'win32':
356
            os.remove(inv_fname)
357
        os.rename(tmpfname, inv_fname)
14 by mbp at sourcefrog
write inventory to temporary file and atomically replace
358
        mutter('wrote working inventory')
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
359
            
1 by mbp at sourcefrog
import from baz patch-364
360
361
    inventory = property(read_working_inventory, _write_inventory, None,
362
                         """Inventory for the working copy.""")
363
364
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
365
    @with_writelock
493 by Martin Pool
- Merge aaron's merge command
366
    def add(self, files, verbose=False, ids=None):
1 by mbp at sourcefrog
import from baz patch-364
367
        """Make files versioned.
368
247 by mbp at sourcefrog
doc
369
        Note that the command line normally calls smart_add instead.
370
1 by mbp at sourcefrog
import from baz patch-364
371
        This puts the files in the Added state, so that they will be
372
        recorded by the next commit.
373
596 by Martin Pool
doc
374
        files
375
            List of paths to add, relative to the base of the tree.
376
377
        ids
378
            If set, use these instead of automatically generated ids.
379
            Must be the same length as the list of files, but may
380
            contain None for ids that are to be autogenerated.
381
254 by Martin Pool
- Doc cleanups from Magnus Therning
382
        TODO: Perhaps have an option to add the ids even if the files do
596 by Martin Pool
doc
383
              not (yet) exist.
1 by mbp at sourcefrog
import from baz patch-364
384
254 by Martin Pool
- Doc cleanups from Magnus Therning
385
        TODO: Perhaps return the ids of the files?  But then again it
596 by Martin Pool
doc
386
              is easy to retrieve them if they're needed.
1 by mbp at sourcefrog
import from baz patch-364
387
254 by Martin Pool
- Doc cleanups from Magnus Therning
388
        TODO: Adding a directory should optionally recurse down and
596 by Martin Pool
doc
389
              add all non-ignored children.  Perhaps do that in a
390
              higher-level method.
1 by mbp at sourcefrog
import from baz patch-364
391
        """
392
        # TODO: Re-adding a file that is removed in the working copy
393
        # should probably put it back with the previous ID.
394
        if isinstance(files, types.StringTypes):
493 by Martin Pool
- Merge aaron's merge command
395
            assert(ids is None or isinstance(ids, types.StringTypes))
1 by mbp at sourcefrog
import from baz patch-364
396
            files = [files]
493 by Martin Pool
- Merge aaron's merge command
397
            if ids is not None:
398
                ids = [ids]
399
400
        if ids is None:
401
            ids = [None] * len(files)
402
        else:
403
            assert(len(ids) == len(files))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
404
1 by mbp at sourcefrog
import from baz patch-364
405
        inv = self.read_working_inventory()
493 by Martin Pool
- Merge aaron's merge command
406
        for f,file_id in zip(files, ids):
1 by mbp at sourcefrog
import from baz patch-364
407
            if is_control_file(f):
576 by Martin Pool
- raise exceptions rather than using bailout()
408
                raise BzrError("cannot add control file %s" % quotefn(f))
1 by mbp at sourcefrog
import from baz patch-364
409
410
            fp = splitpath(f)
411
412
            if len(fp) == 0:
576 by Martin Pool
- raise exceptions rather than using bailout()
413
                raise BzrError("cannot add top-level %r" % f)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
414
67 by mbp at sourcefrog
use abspath() for the function that makes an absolute
415
            fullpath = os.path.normpath(self.abspath(f))
1 by mbp at sourcefrog
import from baz patch-364
416
70 by mbp at sourcefrog
Prepare for smart recursive add.
417
            try:
418
                kind = file_kind(fullpath)
419
            except OSError:
420
                # maybe something better?
576 by Martin Pool
- raise exceptions rather than using bailout()
421
                raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
422
70 by mbp at sourcefrog
Prepare for smart recursive add.
423
            if kind != 'file' and kind != 'directory':
576 by Martin Pool
- raise exceptions rather than using bailout()
424
                raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
70 by mbp at sourcefrog
Prepare for smart recursive add.
425
493 by Martin Pool
- Merge aaron's merge command
426
            if file_id is None:
427
                file_id = gen_file_id(f)
70 by mbp at sourcefrog
Prepare for smart recursive add.
428
            inv.add_path(f, kind=kind, file_id=file_id)
429
1 by mbp at sourcefrog
import from baz patch-364
430
            if verbose:
431
                show_status('A', kind, quotefn(f))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
432
70 by mbp at sourcefrog
Prepare for smart recursive add.
433
            mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
434
435
        self._write_inventory(inv)
70 by mbp at sourcefrog
Prepare for smart recursive add.
436
            
1 by mbp at sourcefrog
import from baz patch-364
437
176 by mbp at sourcefrog
New cat command contributed by janmar.
438
    def print_file(self, file, revno):
439
        """Print `file` to stdout."""
440
        tree = self.revision_tree(self.lookup_revision(revno))
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
441
        # use inventory as it was in that revision
442
        file_id = tree.inventory.path2id(file)
443
        if not file_id:
576 by Martin Pool
- raise exceptions rather than using bailout()
444
            raise BzrError("%r is not present in revision %d" % (file, revno))
178 by mbp at sourcefrog
- Use a non-null file_id for the branch root directory. At the moment
445
        tree.print_file(file_id)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
446
447
448
    @with_writelock
1 by mbp at sourcefrog
import from baz patch-364
449
    def remove(self, files, verbose=False):
450
        """Mark nominated files for removal from the inventory.
451
452
        This does not remove their text.  This does not run on 
453
254 by Martin Pool
- Doc cleanups from Magnus Therning
454
        TODO: Refuse to remove modified files unless --force is given?
1 by mbp at sourcefrog
import from baz patch-364
455
254 by Martin Pool
- Doc cleanups from Magnus Therning
456
        TODO: Do something useful with directories.
1 by mbp at sourcefrog
import from baz patch-364
457
254 by Martin Pool
- Doc cleanups from Magnus Therning
458
        TODO: Should this remove the text or not?  Tough call; not
1 by mbp at sourcefrog
import from baz patch-364
459
        removing may be useful and the user can just use use rm, and
460
        is the opposite of add.  Removing it is consistent with most
461
        other tools.  Maybe an option.
462
        """
463
        ## TODO: Normalize names
464
        ## TODO: Remove nested loops; better scalability
465
        if isinstance(files, types.StringTypes):
466
            files = [files]
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
467
29 by Martin Pool
When removing files, new status should be I or ?, not D
468
        tree = self.working_tree()
469
        inv = tree.inventory
1 by mbp at sourcefrog
import from baz patch-364
470
471
        # do this before any modifications
472
        for f in files:
473
            fid = inv.path2id(f)
474
            if not fid:
576 by Martin Pool
- raise exceptions rather than using bailout()
475
                raise BzrError("cannot remove unversioned file %s" % quotefn(f))
1 by mbp at sourcefrog
import from baz patch-364
476
            mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
477
            if verbose:
29 by Martin Pool
When removing files, new status should be I or ?, not D
478
                # having remove it, it must be either ignored or unknown
479
                if tree.is_ignored(f):
480
                    new_status = 'I'
481
                else:
482
                    new_status = '?'
483
                show_status(new_status, inv[fid].kind, quotefn(f))
1 by mbp at sourcefrog
import from baz patch-364
484
            del inv[fid]
485
486
        self._write_inventory(inv)
487
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
488
493 by Martin Pool
- Merge aaron's merge command
489
    def set_inventory(self, new_inventory_list):
490
        inv = Inventory()
491
        for path, file_id, parent, kind in new_inventory_list:
492
            name = os.path.basename(path)
493
            if name == "":
494
                continue
495
            inv.add(InventoryEntry(file_id, name, kind, parent))
496
        self._write_inventory(inv)
497
1 by mbp at sourcefrog
import from baz patch-364
498
499
    def unknowns(self):
500
        """Return all unknown files.
501
502
        These are files in the working directory that are not versioned or
503
        control files or ignored.
504
        
505
        >>> b = ScratchBranch(files=['foo', 'foo~'])
506
        >>> list(b.unknowns())
507
        ['foo']
508
        >>> b.add('foo')
509
        >>> list(b.unknowns())
510
        []
511
        >>> b.remove('foo')
512
        >>> list(b.unknowns())
513
        ['foo']
514
        """
515
        return self.working_tree().unknowns()
516
517
233 by mbp at sourcefrog
- more output from test.sh
518
    def append_revision(self, revision_id):
519
        mutter("add {%s} to revision-history" % revision_id)
520
        rev_history = self.revision_history()
521
522
        tmprhname = self.controlfilename('revision-history.tmp')
523
        rhname = self.controlfilename('revision-history')
524
        
525
        f = file(tmprhname, 'wt')
526
        rev_history.append(revision_id)
527
        f.write('\n'.join(rev_history))
528
        f.write('\n')
529
        f.close()
530
531
        if sys.platform == 'win32':
532
            os.remove(rhname)
533
        os.rename(tmprhname, rhname)
534
        
535
536
1 by mbp at sourcefrog
import from baz patch-364
537
    def get_revision(self, revision_id):
538
        """Return the Revision object for a named revision"""
539
        r = Revision.read_xml(self.revision_store[revision_id])
540
        assert r.revision_id == revision_id
541
        return r
542
543
544
    def get_inventory(self, inventory_id):
545
        """Get Inventory object by hash.
546
254 by Martin Pool
- Doc cleanups from Magnus Therning
547
        TODO: Perhaps for this and similar methods, take a revision
1 by mbp at sourcefrog
import from baz patch-364
548
               parameter which can be either an integer revno or a
549
               string hash."""
550
        i = Inventory.read_xml(self.inventory_store[inventory_id])
551
        return i
552
553
554
    def get_revision_inventory(self, revision_id):
555
        """Return inventory of a past revision."""
556
        if revision_id == None:
557
            return Inventory()
558
        else:
559
            return self.get_inventory(self.get_revision(revision_id).inventory_id)
560
561
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
562
    @with_readlock
1 by mbp at sourcefrog
import from baz patch-364
563
    def revision_history(self):
564
        """Return sequence of revision hashes on to this branch.
565
566
        >>> ScratchBranch().revision_history()
567
        []
568
        """
319 by Martin Pool
- remove trivial chomp() function
569
        return [l.rstrip('\r\n') for l in self.controlfile('revision-history', 'r').readlines()]
1 by mbp at sourcefrog
import from baz patch-364
570
571
385 by Martin Pool
- New Branch.enum_history method
572
    def enum_history(self, direction):
573
        """Return (revno, revision_id) for history of branch.
574
575
        direction
576
            'forward' is from earliest to latest
577
            'reverse' is from latest to earliest
578
        """
579
        rh = self.revision_history()
580
        if direction == 'forward':
581
            i = 1
582
            for rid in rh:
583
                yield i, rid
584
                i += 1
585
        elif direction == 'reverse':
586
            i = len(rh)
587
            while i > 0:
588
                yield i, rh[i-1]
589
                i -= 1
590
        else:
526 by Martin Pool
- use ValueError for bad internal parameters
591
            raise ValueError('invalid history direction', direction)
385 by Martin Pool
- New Branch.enum_history method
592
593
1 by mbp at sourcefrog
import from baz patch-364
594
    def revno(self):
595
        """Return current revision number for this branch.
596
597
        That is equivalent to the number of revisions committed to
598
        this branch.
599
        """
600
        return len(self.revision_history())
601
602
603
    def last_patch(self):
604
        """Return last patch hash, or None if no history.
605
        """
606
        ph = self.revision_history()
607
        if ph:
608
            return ph[-1]
184 by mbp at sourcefrog
pychecker fixups
609
        else:
610
            return None
485 by Martin Pool
- move commit code into its own module
611
612
613
    def commit(self, *args, **kw):
614
        """Deprecated"""
615
        from bzrlib.commit import commit
616
        commit(self, *args, **kw)
184 by mbp at sourcefrog
pychecker fixups
617
        
1 by mbp at sourcefrog
import from baz patch-364
618
619
    def lookup_revision(self, revno):
620
        """Return revision hash for revision number."""
621
        if revno == 0:
622
            return None
623
624
        try:
625
            # list is 0-based; revisions are 1-based
626
            return self.revision_history()[revno-1]
627
        except IndexError:
184 by mbp at sourcefrog
pychecker fixups
628
            raise BzrError("no such revision %s" % revno)
1 by mbp at sourcefrog
import from baz patch-364
629
630
631
    def revision_tree(self, revision_id):
632
        """Return Tree for a revision on this branch.
633
634
        `revision_id` may be None for the null revision, in which case
635
        an `EmptyTree` is returned."""
529 by Martin Pool
todo
636
        # TODO: refactor this to use an existing revision object
637
        # so we don't need to read it in twice.
1 by mbp at sourcefrog
import from baz patch-364
638
        if revision_id == None:
639
            return EmptyTree()
640
        else:
641
            inv = self.get_revision_inventory(revision_id)
642
            return RevisionTree(self.text_store, inv)
643
644
645
    def working_tree(self):
646
        """Return a `Tree` for the working copy."""
453 by Martin Pool
- Split WorkingTree into its own file
647
        from workingtree import WorkingTree
1 by mbp at sourcefrog
import from baz patch-364
648
        return WorkingTree(self.base, self.read_working_inventory())
649
650
651
    def basis_tree(self):
652
        """Return `Tree` object for last revision.
653
654
        If there are no revisions yet, return an `EmptyTree`.
655
        """
656
        r = self.last_patch()
657
        if r == None:
658
            return EmptyTree()
659
        else:
660
            return RevisionTree(self.text_store, self.get_revision_inventory(r))
661
662
663
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
664
    @with_writelock
168 by mbp at sourcefrog
new "rename" command
665
    def rename_one(self, from_rel, to_rel):
309 by Martin Pool
doc
666
        """Rename one file.
667
668
        This can change the directory or the filename or both.
353 by Martin Pool
- Per-branch locks in read and write modes.
669
        """
168 by mbp at sourcefrog
new "rename" command
670
        tree = self.working_tree()
671
        inv = tree.inventory
672
        if not tree.has_filename(from_rel):
576 by Martin Pool
- raise exceptions rather than using bailout()
673
            raise BzrError("can't rename: old working file %r does not exist" % from_rel)
168 by mbp at sourcefrog
new "rename" command
674
        if tree.has_filename(to_rel):
576 by Martin Pool
- raise exceptions rather than using bailout()
675
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
676
168 by mbp at sourcefrog
new "rename" command
677
        file_id = inv.path2id(from_rel)
678
        if file_id == None:
576 by Martin Pool
- raise exceptions rather than using bailout()
679
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
168 by mbp at sourcefrog
new "rename" command
680
681
        if inv.path2id(to_rel):
576 by Martin Pool
- raise exceptions rather than using bailout()
682
            raise BzrError("can't rename: new name %r is already versioned" % to_rel)
168 by mbp at sourcefrog
new "rename" command
683
684
        to_dir, to_tail = os.path.split(to_rel)
685
        to_dir_id = inv.path2id(to_dir)
686
        if to_dir_id == None and to_dir != '':
576 by Martin Pool
- raise exceptions rather than using bailout()
687
            raise BzrError("can't determine destination directory id for %r" % to_dir)
168 by mbp at sourcefrog
new "rename" command
688
689
        mutter("rename_one:")
690
        mutter("  file_id    {%s}" % file_id)
691
        mutter("  from_rel   %r" % from_rel)
692
        mutter("  to_rel     %r" % to_rel)
693
        mutter("  to_dir     %r" % to_dir)
694
        mutter("  to_dir_id  {%s}" % to_dir_id)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
695
168 by mbp at sourcefrog
new "rename" command
696
        inv.rename(file_id, to_dir_id, to_tail)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
697
698
        print "%s => %s" % (from_rel, to_rel)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
699
171 by mbp at sourcefrog
better error message when working file rename fails
700
        from_abs = self.abspath(from_rel)
701
        to_abs = self.abspath(to_rel)
702
        try:
703
            os.rename(from_abs, to_abs)
704
        except OSError, e:
576 by Martin Pool
- raise exceptions rather than using bailout()
705
            raise BzrError("failed to rename %r to %r: %s"
171 by mbp at sourcefrog
better error message when working file rename fails
706
                    % (from_abs, to_abs, e[1]),
707
                    ["rename rolled back"])
168 by mbp at sourcefrog
new "rename" command
708
709
        self._write_inventory(inv)
580 by Martin Pool
- Use explicit lock methods on a branch, rather than doing it
710
711
712
713
    @with_writelock
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
714
    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
715
        """Rename files.
716
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
717
        to_name must exist as a versioned directory.
718
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
719
        If to_name exists and is a directory, the files are moved into
720
        it, keeping their old names.  If it is a directory, 
721
722
        Note that to_name is only the last component of the new name;
723
        this doesn't change the directory.
724
        """
725
        ## TODO: Option to move IDs only
726
        assert not isinstance(from_paths, basestring)
727
        tree = self.working_tree()
728
        inv = tree.inventory
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
729
        to_abs = self.abspath(to_name)
730
        if not isdir(to_abs):
576 by Martin Pool
- raise exceptions rather than using bailout()
731
            raise BzrError("destination %r is not a directory" % to_abs)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
732
        if not tree.has_filename(to_name):
576 by Martin Pool
- raise exceptions rather than using bailout()
733
            raise BzrError("destination %r not in working directory" % to_abs)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
734
        to_dir_id = inv.path2id(to_name)
735
        if to_dir_id == None and to_name != '':
576 by Martin Pool
- raise exceptions rather than using bailout()
736
            raise BzrError("destination %r is not a versioned directory" % to_name)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
737
        to_dir_ie = inv[to_dir_id]
175 by mbp at sourcefrog
fix up moving files into branch root
738
        if to_dir_ie.kind not in ('directory', 'root_directory'):
576 by Martin Pool
- raise exceptions rather than using bailout()
739
            raise BzrError("destination %r is not a directory" % to_abs)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
740
540 by Martin Pool
- use builtin set object in python2.4
741
        to_idpath = inv.get_idpath(to_dir_id)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
742
743
        for f in from_paths:
744
            if not tree.has_filename(f):
576 by Martin Pool
- raise exceptions rather than using bailout()
745
                raise BzrError("%r does not exist in working tree" % f)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
746
            f_id = inv.path2id(f)
747
            if f_id == None:
576 by Martin Pool
- raise exceptions rather than using bailout()
748
                raise BzrError("%r is not versioned" % f)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
749
            name_tail = splitpath(f)[-1]
750
            dest_path = appendpath(to_name, name_tail)
751
            if tree.has_filename(dest_path):
576 by Martin Pool
- raise exceptions rather than using bailout()
752
                raise BzrError("destination %r already exists" % dest_path)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
753
            if f_id in to_idpath:
576 by Martin Pool
- raise exceptions rather than using bailout()
754
                raise BzrError("can't move %r to a subdirectory of itself" % f)
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
755
756
        # OK, so there's a race here, it's possible that someone will
757
        # create a file in this interval and then the rename might be
758
        # left half-done.  But we should have caught most problems.
759
760
        for f in from_paths:
761
            name_tail = splitpath(f)[-1]
762
            dest_path = appendpath(to_name, name_tail)
763
            print "%s => %s" % (f, dest_path)
764
            inv.rename(inv.path2id(f), to_dir_id, name_tail)
765
            try:
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
766
                os.rename(self.abspath(f), self.abspath(dest_path))
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
767
            except OSError, e:
576 by Martin Pool
- raise exceptions rather than using bailout()
768
                raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
174 by mbp at sourcefrog
- New 'move' command; now separated out from rename
769
                        ["rename rolled back"])
770
771
        self._write_inventory(inv)
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
772
773
774
1 by mbp at sourcefrog
import from baz patch-364
775
776
class ScratchBranch(Branch):
777
    """Special test class: a branch that cleans up after itself.
778
779
    >>> b = ScratchBranch()
780
    >>> isdir(b.base)
781
    True
782
    >>> bd = b.base
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
783
    >>> b.destroy()
1 by mbp at sourcefrog
import from baz patch-364
784
    >>> isdir(bd)
785
    False
786
    """
100 by mbp at sourcefrog
- add test case for ignore files
787
    def __init__(self, files=[], dirs=[]):
1 by mbp at sourcefrog
import from baz patch-364
788
        """Make a test branch.
789
790
        This creates a temporary directory and runs init-tree in it.
791
792
        If any files are listed, they are created in the working copy.
793
        """
794
        Branch.__init__(self, tempfile.mkdtemp(), init=True)
100 by mbp at sourcefrog
- add test case for ignore files
795
        for d in dirs:
796
            os.mkdir(self.abspath(d))
797
            
1 by mbp at sourcefrog
import from baz patch-364
798
        for f in files:
799
            file(os.path.join(self.base, f), 'w').write('content of %s' % f)
800
801
802
    def __del__(self):
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
803
        self.destroy()
804
805
    def destroy(self):
1 by mbp at sourcefrog
import from baz patch-364
806
        """Destroy the test branch, removing the scratch directory."""
163 by mbp at sourcefrog
merge win32 portability fixes
807
        try:
610 by Martin Pool
- replace Branch.lock(mode) with separate lock_read and lock_write
808
            if self.base:
809
                mutter("delete ScratchBranch %s" % self.base)
810
                shutil.rmtree(self.base)
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
811
        except OSError, e:
163 by mbp at sourcefrog
merge win32 portability fixes
812
            # Work around for shutil.rmtree failing on Windows when
813
            # readonly files are encountered
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
814
            mutter("hit exception in destroying ScratchBranch: %s" % e)
163 by mbp at sourcefrog
merge win32 portability fixes
815
            for root, dirs, files in os.walk(self.base, topdown=False):
816
                for name in files:
817
                    os.chmod(os.path.join(root, name), 0700)
818
            shutil.rmtree(self.base)
396 by Martin Pool
- Using the destructor on a ScratchBranch is not reliable;
819
        self.base = None
1 by mbp at sourcefrog
import from baz patch-364
820
821
    
822
823
######################################################################
824
# predicates
825
826
827
def is_control_file(filename):
828
    ## FIXME: better check
829
    filename = os.path.normpath(filename)
830
    while filename != '':
831
        head, tail = os.path.split(filename)
832
        ## mutter('check %r for control file' % ((head, tail), ))
833
        if tail == bzrlib.BZRDIR:
834
            return True
70 by mbp at sourcefrog
Prepare for smart recursive add.
835
        if filename == head:
836
            break
1 by mbp at sourcefrog
import from baz patch-364
837
        filename = head
838
    return False
839
840
841
70 by mbp at sourcefrog
Prepare for smart recursive add.
842
def gen_file_id(name):
1 by mbp at sourcefrog
import from baz patch-364
843
    """Return new file id.
844
845
    This should probably generate proper UUIDs, but for the moment we
846
    cope with just randomness because running uuidgen every time is
847
    slow."""
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
848
    import re
849
850
    # get last component
70 by mbp at sourcefrog
Prepare for smart recursive add.
851
    idx = name.rfind('/')
852
    if idx != -1:
853
        name = name[idx+1 : ]
262 by Martin Pool
- gen_file_id: break the file on either / or \ when looking
854
    idx = name.rfind('\\')
855
    if idx != -1:
856
        name = name[idx+1 : ]
70 by mbp at sourcefrog
Prepare for smart recursive add.
857
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
858
    # make it not a hidden file
70 by mbp at sourcefrog
Prepare for smart recursive add.
859
    name = name.lstrip('.')
860
535 by Martin Pool
- try to eliminate wierd characters from file names when they're
861
    # remove any wierd characters; we don't escape them but rather
862
    # just pull them out
863
    name = re.sub(r'[^\w.]', '', name)
864
190 by mbp at sourcefrog
64 bits of randomness in file/revision ids
865
    s = hexlify(rand_bytes(8))
1 by mbp at sourcefrog
import from baz patch-364
866
    return '-'.join((name, compact_date(time.time()), s))