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