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