43
43
return remotebranch.RemoteBranch(f, **args)
45
45
return Branch(f, **args)
49
def _relpath(base, path):
50
"""Return path relative to base, or raise exception.
52
The path may be either an absolute path or a path relative to the
53
current working directory.
55
Lifted out of Branch.relpath for ease of testing.
57
os.path.commonprefix (python2.4) has a bad bug that it works just
58
on string prefixes, assuming that '/u' is a prefix of '/u2'. This
59
avoids that problem."""
60
rp = os.path.abspath(path)
64
while len(head) >= len(base):
67
head, tail = os.path.split(head)
71
from errors import NotBranchError
72
raise NotBranchError("path %r is not within branch %r" % (rp, base))
77
48
def find_branch_root(f=None):
109
80
######################################################################
112
class Branch(object):
113
84
"""Branch holding a history of revisions.
116
87
Base directory of the branch.
122
If _lock_mode is true, a positive count of the number of times the
126
Lock object from bzrlib.lock.
132
def __init__(self, base, init=False, find_root=True):
91
def __init__(self, base, init=False, find_root=True, lock_mode='w'):
133
92
"""Create new branch object at a particular location.
135
94
base -- Base directory for the branch.
153
112
self.base = os.path.realpath(base)
154
113
if not isdir(self.controlfilename('.')):
155
from errors import NotBranchError
156
raise NotBranchError("not a bzr branch: %s" % quotefn(base),
157
['use "bzr init" to initialize a new working tree',
158
'current bzr can only operate from top-of-tree'])
114
bailout("not a bzr branch: %s" % quotefn(base),
115
['use "bzr init" to initialize a new working tree',
116
'current bzr can only operate from top-of-tree'])
159
117
self._check_format()
160
self._lockfile = self.controlfile('branch-lock', 'wb')
162
120
self.text_store = ImmutableStore(self.controlfilename('text-store'))
163
121
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
171
129
__repr__ = __str__
176
from warnings import warn
177
warn("branch %r was not explicitly unlocked" % self)
182
def lock_write(self):
184
if self._lock_mode != 'w':
185
from errors import LockError
186
raise LockError("can't upgrade to a write lock from %r" %
188
self._lock_count += 1
190
from bzrlib.lock import lock, LOCK_EX
192
lock(self._lockfile, LOCK_EX)
193
self._lock_mode = 'w'
200
assert self._lock_mode in ('r', 'w'), \
201
"invalid lock mode %r" % self._lock_mode
202
self._lock_count += 1
204
from bzrlib.lock import lock, LOCK_SH
206
lock(self._lockfile, LOCK_SH)
207
self._lock_mode = 'r'
133
def lock(self, mode='w'):
134
"""Lock the on-disk branch, excluding other processes."""
140
om = os.O_WRONLY | os.O_CREAT
145
raise BzrError("invalid locking mode %r" % mode)
148
lockfile = os.open(self.controlfilename('branch-lock'), om)
150
if e.errno == errno.ENOENT:
151
# might not exist on branches from <0.0.4
152
self.controlfile('branch-lock', 'w').close()
153
lockfile = os.open(self.controlfilename('branch-lock'), om)
213
if not self._lock_mode:
214
from errors import LockError
215
raise LockError('branch %r is not locked' % (self))
217
if self._lock_count > 1:
218
self._lock_count -= 1
220
assert self._lock_count == 1
221
from bzrlib.lock import unlock
222
unlock(self._lockfile)
223
self._lock_mode = self._lock_count = None
157
fcntl.lockf(lockfile, lm)
159
fcntl.lockf(lockfile, fcntl.LOCK_UN)
161
self._lockmode = None
163
self._lockmode = mode
165
warning("please write a locking method for platform %r" % sys.platform)
167
self._lockmode = None
169
self._lockmode = mode
172
def _need_readlock(self):
173
if self._lockmode not in ['r', 'w']:
174
raise BzrError('need read lock on branch, only have %r' % self._lockmode)
176
def _need_writelock(self):
177
if self._lockmode not in ['w']:
178
raise BzrError('need write lock on branch, only have %r' % self._lockmode)
226
181
def abspath(self, name):
299
260
fmt = self.controlfile('branch-format', 'r').read()
300
261
fmt.replace('\r\n', '')
301
262
if fmt != BZR_BRANCH_FORMAT:
302
raise BzrError('sorry, branch format %r not supported' % fmt,
303
['use a different bzr version',
304
'or remove the .bzr directory and "bzr init" again'])
263
bailout('sorry, branch format %r not supported' % fmt,
264
['use a different bzr version',
265
'or remove the .bzr directory and "bzr init" again'])
308
268
def read_working_inventory(self):
309
269
"""Read the working inventory."""
270
self._need_readlock()
310
271
before = time.time()
311
272
# ElementTree does its own conversion from UTF-8, so open in
315
inv = Inventory.read_xml(self.controlfile('inventory', 'rb'))
316
mutter("loaded inventory of %d items in %f"
317
% (len(inv), time.time() - before))
274
inv = Inventory.read_xml(self.controlfile('inventory', 'rb'))
275
mutter("loaded inventory of %d items in %f"
276
% (len(inv), time.time() - before))
323
280
def _write_inventory(self, inv):
324
281
"""Update the working inventory.
351
309
This puts the files in the Added state, so that they will be
352
310
recorded by the next commit.
355
List of paths to add, relative to the base of the tree.
358
If set, use these instead of automatically generated ids.
359
Must be the same length as the list of files, but may
360
contain None for ids that are to be autogenerated.
362
312
TODO: Perhaps have an option to add the ids even if the files do
365
315
TODO: Perhaps return the ids of the files? But then again it
366
is easy to retrieve them if they're needed.
316
is easy to retrieve them if they're needed.
318
TODO: Option to specify file id.
368
320
TODO: Adding a directory should optionally recurse down and
369
add all non-ignored children. Perhaps do that in a
321
add all non-ignored children. Perhaps do that in a
324
self._need_writelock()
372
326
# TODO: Re-adding a file that is removed in the working copy
373
327
# should probably put it back with the previous ID.
374
328
if isinstance(files, types.StringTypes):
381
335
ids = [None] * len(files)
383
337
assert(len(ids) == len(files))
387
inv = self.read_working_inventory()
388
for f,file_id in zip(files, ids):
389
if is_control_file(f):
390
raise BzrError("cannot add control file %s" % quotefn(f))
395
raise BzrError("cannot add top-level %r" % f)
397
fullpath = os.path.normpath(self.abspath(f))
400
kind = file_kind(fullpath)
402
# maybe something better?
403
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
405
if kind != 'file' and kind != 'directory':
406
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
409
file_id = gen_file_id(f)
410
inv.add_path(f, kind=kind, file_id=file_id)
413
show_status('A', kind, quotefn(f))
415
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
417
self._write_inventory(inv)
339
inv = self.read_working_inventory()
340
for f,file_id in zip(files, ids):
341
if is_control_file(f):
342
bailout("cannot add control file %s" % quotefn(f))
347
bailout("cannot add top-level %r" % f)
349
fullpath = os.path.normpath(self.abspath(f))
352
kind = file_kind(fullpath)
354
# maybe something better?
355
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
357
if kind != 'file' and kind != 'directory':
358
bailout('cannot add: not a regular file or directory: %s' % quotefn(f))
361
file_id = gen_file_id(f)
362
inv.add_path(f, kind=kind, file_id=file_id)
365
show_status('A', kind, quotefn(f))
367
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
369
self._write_inventory(inv)
422
372
def print_file(self, file, revno):
423
373
"""Print `file` to stdout."""
426
tree = self.revision_tree(self.lookup_revision(revno))
427
# use inventory as it was in that revision
428
file_id = tree.inventory.path2id(file)
430
raise BzrError("%r is not present in revision %d" % (file, revno))
431
tree.print_file(file_id)
374
self._need_readlock()
375
tree = self.revision_tree(self.lookup_revision(revno))
376
# use inventory as it was in that revision
377
file_id = tree.inventory.path2id(file)
379
bailout("%r is not present in revision %d" % (file, revno))
380
tree.print_file(file_id)
436
383
def remove(self, files, verbose=False):
437
384
"""Mark nominated files for removal from the inventory.
450
397
## TODO: Normalize names
451
398
## TODO: Remove nested loops; better scalability
399
self._need_writelock()
452
401
if isinstance(files, types.StringTypes):
458
tree = self.working_tree()
461
# do this before any modifications
465
raise BzrError("cannot remove unversioned file %s" % quotefn(f))
466
mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
468
# having remove it, it must be either ignored or unknown
469
if tree.is_ignored(f):
473
show_status(new_status, inv[fid].kind, quotefn(f))
476
self._write_inventory(inv)
481
# FIXME: this doesn't need to be a branch method
404
tree = self.working_tree()
407
# do this before any modifications
411
bailout("cannot remove unversioned file %s" % quotefn(f))
412
mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
414
# having remove it, it must be either ignored or unknown
415
if tree.is_ignored(f):
419
show_status(new_status, inv[fid].kind, quotefn(f))
422
self._write_inventory(inv)
482
424
def set_inventory(self, new_inventory_list):
483
425
inv = Inventory()
484
426
for path, file_id, parent, kind in new_inventory_list:
664
606
This can change the directory or the filename or both.
608
self._need_writelock()
609
tree = self.working_tree()
611
if not tree.has_filename(from_rel):
612
bailout("can't rename: old working file %r does not exist" % from_rel)
613
if tree.has_filename(to_rel):
614
bailout("can't rename: new working file %r already exists" % to_rel)
616
file_id = inv.path2id(from_rel)
618
bailout("can't rename: old name %r is not versioned" % from_rel)
620
if inv.path2id(to_rel):
621
bailout("can't rename: new name %r is already versioned" % to_rel)
623
to_dir, to_tail = os.path.split(to_rel)
624
to_dir_id = inv.path2id(to_dir)
625
if to_dir_id == None and to_dir != '':
626
bailout("can't determine destination directory id for %r" % to_dir)
628
mutter("rename_one:")
629
mutter(" file_id {%s}" % file_id)
630
mutter(" from_rel %r" % from_rel)
631
mutter(" to_rel %r" % to_rel)
632
mutter(" to_dir %r" % to_dir)
633
mutter(" to_dir_id {%s}" % to_dir_id)
635
inv.rename(file_id, to_dir_id, to_tail)
637
print "%s => %s" % (from_rel, to_rel)
639
from_abs = self.abspath(from_rel)
640
to_abs = self.abspath(to_rel)
668
tree = self.working_tree()
670
if not tree.has_filename(from_rel):
671
raise BzrError("can't rename: old working file %r does not exist" % from_rel)
672
if tree.has_filename(to_rel):
673
raise BzrError("can't rename: new working file %r already exists" % to_rel)
675
file_id = inv.path2id(from_rel)
677
raise BzrError("can't rename: old name %r is not versioned" % from_rel)
679
if inv.path2id(to_rel):
680
raise BzrError("can't rename: new name %r is already versioned" % to_rel)
682
to_dir, to_tail = os.path.split(to_rel)
683
to_dir_id = inv.path2id(to_dir)
684
if to_dir_id == None and to_dir != '':
685
raise BzrError("can't determine destination directory id for %r" % to_dir)
687
mutter("rename_one:")
688
mutter(" file_id {%s}" % file_id)
689
mutter(" from_rel %r" % from_rel)
690
mutter(" to_rel %r" % to_rel)
691
mutter(" to_dir %r" % to_dir)
692
mutter(" to_dir_id {%s}" % to_dir_id)
694
inv.rename(file_id, to_dir_id, to_tail)
696
print "%s => %s" % (from_rel, to_rel)
698
from_abs = self.abspath(from_rel)
699
to_abs = self.abspath(to_rel)
701
os.rename(from_abs, to_abs)
703
raise BzrError("failed to rename %r to %r: %s"
704
% (from_abs, to_abs, e[1]),
705
["rename rolled back"])
707
self._write_inventory(inv)
642
os.rename(from_abs, to_abs)
644
bailout("failed to rename %r to %r: %s"
645
% (from_abs, to_abs, e[1]),
646
["rename rolled back"])
648
self._write_inventory(inv)
712
652
def move(self, from_paths, to_name):
720
660
Note that to_name is only the last component of the new name;
721
661
this doesn't change the directory.
725
## TODO: Option to move IDs only
726
assert not isinstance(from_paths, basestring)
727
tree = self.working_tree()
729
to_abs = self.abspath(to_name)
730
if not isdir(to_abs):
731
raise BzrError("destination %r is not a directory" % to_abs)
732
if not tree.has_filename(to_name):
733
raise BzrError("destination %r not in working directory" % to_abs)
734
to_dir_id = inv.path2id(to_name)
735
if to_dir_id == None and to_name != '':
736
raise BzrError("destination %r is not a versioned directory" % to_name)
737
to_dir_ie = inv[to_dir_id]
738
if to_dir_ie.kind not in ('directory', 'root_directory'):
739
raise BzrError("destination %r is not a directory" % to_abs)
741
to_idpath = inv.get_idpath(to_dir_id)
744
if not tree.has_filename(f):
745
raise BzrError("%r does not exist in working tree" % f)
746
f_id = inv.path2id(f)
748
raise BzrError("%r is not versioned" % f)
749
name_tail = splitpath(f)[-1]
750
dest_path = appendpath(to_name, name_tail)
751
if tree.has_filename(dest_path):
752
raise BzrError("destination %r already exists" % dest_path)
753
if f_id in to_idpath:
754
raise BzrError("can't move %r to a subdirectory of itself" % f)
756
# OK, so there's a race here, it's possible that someone will
757
# create a file in this interval and then the rename might be
758
# left half-done. But we should have caught most problems.
761
name_tail = splitpath(f)[-1]
762
dest_path = appendpath(to_name, name_tail)
763
print "%s => %s" % (f, dest_path)
764
inv.rename(inv.path2id(f), to_dir_id, name_tail)
766
os.rename(self.abspath(f), self.abspath(dest_path))
768
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
769
["rename rolled back"])
771
self._write_inventory(inv)
663
self._need_writelock()
664
## TODO: Option to move IDs only
665
assert not isinstance(from_paths, basestring)
666
tree = self.working_tree()
668
to_abs = self.abspath(to_name)
669
if not isdir(to_abs):
670
bailout("destination %r is not a directory" % to_abs)
671
if not tree.has_filename(to_name):
672
bailout("destination %r not in working directory" % to_abs)
673
to_dir_id = inv.path2id(to_name)
674
if to_dir_id == None and to_name != '':
675
bailout("destination %r is not a versioned directory" % to_name)
676
to_dir_ie = inv[to_dir_id]
677
if to_dir_ie.kind not in ('directory', 'root_directory'):
678
bailout("destination %r is not a directory" % to_abs)
680
to_idpath = inv.get_idpath(to_dir_id)
683
if not tree.has_filename(f):
684
bailout("%r does not exist in working tree" % f)
685
f_id = inv.path2id(f)
687
bailout("%r is not versioned" % f)
688
name_tail = splitpath(f)[-1]
689
dest_path = appendpath(to_name, name_tail)
690
if tree.has_filename(dest_path):
691
bailout("destination %r already exists" % dest_path)
692
if f_id in to_idpath:
693
bailout("can't move %r to a subdirectory of itself" % f)
695
# OK, so there's a race here, it's possible that someone will
696
# create a file in this interval and then the rename might be
697
# left half-done. But we should have caught most problems.
700
name_tail = splitpath(f)[-1]
701
dest_path = appendpath(to_name, name_tail)
702
print "%s => %s" % (f, dest_path)
703
inv.rename(inv.path2id(f), to_dir_id, name_tail)
705
os.rename(self.abspath(f), self.abspath(dest_path))
707
bailout("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
708
["rename rolled back"])
710
self._write_inventory(inv)