137
84
"""Branch holding a history of revisions.
140
Base directory of the branch.
146
If _lock_mode is true, a positive count of the number of times the
150
Lock object from bzrlib.lock.
87
Base directory/url of the branch.
89
hooks: An instance of BranchHooks.
91
# this is really an instance variable - FIXME move it there
157
# Map some sort of prefix into a namespace
158
# stuff like "revno:10", "revid:", etc.
159
# This should match a prefix with a function which accepts
160
REVISION_NAMESPACES = {}
162
def __init__(self, base, init=False, find_root=True):
163
"""Create new branch object at a particular location.
165
base -- Base directory for the branch.
167
init -- If True, create new control files in a previously
168
unversioned directory. If False, the branch must already
171
find_root -- If true and init is false, find the root of the
172
existing branch containing base.
174
In the test suite, creation of new trees is tested using the
175
`ScratchBranch` class.
177
from bzrlib.store import ImmutableStore
179
self.base = os.path.realpath(base)
182
self.base = find_branch_root(base)
184
self.base = os.path.realpath(base)
185
if not isdir(self.controlfilename('.')):
186
from errors import NotBranchError
187
raise NotBranchError("not a bzr branch: %s" % quotefn(base),
188
['use "bzr init" to initialize a new working tree',
189
'current bzr can only operate from top-of-tree'])
192
self.text_store = ImmutableStore(self.controlfilename('text-store'))
193
self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
194
self.inventory_store = ImmutableStore(self.controlfilename('inventory-store'))
198
return '%s(%r)' % (self.__class__.__name__, self.base)
205
if self._lock_mode or self._lock:
206
from warnings import warn
207
warn("branch %r was not explicitly unlocked" % self)
95
# override this to set the strategy for storing tags
97
return DisabledTags(self)
99
def __init__(self, *ignored, **ignored_too):
100
self.tags = self._make_tags()
102
def break_lock(self):
103
"""Break a lock if one is present from another instance.
105
Uses the ui factory to ask for confirmation if the lock may be from
108
This will probe the repository for its lock as well.
110
self.control_files.break_lock()
111
self.repository.break_lock()
112
master = self.get_master_branch()
113
if master is not None:
117
@deprecated_method(zero_eight)
118
def open_downlevel(base):
119
"""Open a branch which may be of an old format."""
120
return Branch.open(base, _unsupported=True)
123
def open(base, _unsupported=False):
124
"""Open the branch rooted at base.
126
For instance, if the branch is at URL/.bzr/branch,
127
Branch.open(URL) -> a Branch instance.
129
control = bzrdir.BzrDir.open(base, _unsupported)
130
return control.open_branch(_unsupported)
133
def open_containing(url):
134
"""Open an existing branch which contains url.
136
This probes for a branch at url, and searches upwards from there.
138
Basically we keep looking up until we find the control directory or
139
run into the root. If there isn't one, raises NotBranchError.
140
If there is one and it is either an unrecognised format or an unsupported
141
format, UnknownFormatError or UnsupportedFormatError are raised.
142
If there is one, it is returned, along with the unused portion of url.
144
control, relpath = bzrdir.BzrDir.open_containing(url)
145
return control.open_branch(), relpath
148
@deprecated_function(zero_eight)
149
def initialize(base):
150
"""Create a new working tree and branch, rooted at 'base' (url)
152
NOTE: This will soon be deprecated in favour of creation
155
return bzrdir.BzrDir.create_standalone_workingtree(base).branch
157
@deprecated_function(zero_eight)
158
def setup_caching(self, cache_root):
159
"""Subclasses that care about caching should override this, and set
160
up cached stores located under cache_root.
162
NOTE: This is unused.
166
def get_config(self):
167
return BranchConfig(self)
170
return self.get_config().get_nickname()
172
def _set_nick(self, nick):
173
self.get_config().set_user_option('nickname', nick)
175
nick = property(_get_nick, _set_nick)
178
raise NotImplementedError(self.is_locked)
212
180
def lock_write(self):
214
if self._lock_mode != 'w':
215
from errors import LockError
216
raise LockError("can't upgrade to a write lock from %r" %
218
self._lock_count += 1
220
from bzrlib.lock import WriteLock
222
self._lock = WriteLock(self.controlfilename('branch-lock'))
223
self._lock_mode = 'w'
181
raise NotImplementedError(self.lock_write)
228
183
def lock_read(self):
230
assert self._lock_mode in ('r', 'w'), \
231
"invalid lock mode %r" % self._lock_mode
232
self._lock_count += 1
234
from bzrlib.lock import ReadLock
236
self._lock = ReadLock(self.controlfilename('branch-lock'))
237
self._lock_mode = 'r'
184
raise NotImplementedError(self.lock_read)
242
186
def unlock(self):
243
if not self._lock_mode:
244
from errors import LockError
245
raise LockError('branch %r is not locked' % (self))
247
if self._lock_count > 1:
248
self._lock_count -= 1
252
self._lock_mode = self._lock_count = None
187
raise NotImplementedError(self.unlock)
189
def peek_lock_mode(self):
190
"""Return lock mode for the Branch: 'r', 'w' or None"""
191
raise NotImplementedError(self.peek_lock_mode)
193
def get_physical_lock_status(self):
194
raise NotImplementedError(self.get_physical_lock_status)
255
196
def abspath(self, name):
256
"""Return absolute filename for something in the branch"""
257
return os.path.join(self.base, name)
260
def relpath(self, path):
261
"""Return path relative to this branch of something inside it.
263
Raises an error if path is not in this branch."""
264
return _relpath(self.base, path)
267
def controlfilename(self, file_or_path):
268
"""Return location relative to branch."""
269
if isinstance(file_or_path, basestring):
270
file_or_path = [file_or_path]
271
return os.path.join(self.base, bzrlib.BZRDIR, *file_or_path)
274
def controlfile(self, file_or_path, mode='r'):
275
"""Open a control file for this branch.
277
There are two classes of file in the control directory: text
278
and binary. binary files are untranslated byte streams. Text
279
control files are stored with Unix newlines and in UTF-8, even
280
if the platform or locale defaults are different.
282
Controlfiles should almost never be opened in write mode but
283
rather should be atomically copied and replaced using atomicfile.
286
fn = self.controlfilename(file_or_path)
288
if mode == 'rb' or mode == 'wb':
289
return file(fn, mode)
290
elif mode == 'r' or mode == 'w':
291
# open in binary mode anyhow so there's no newline translation;
292
# codecs uses line buffering by default; don't want that.
294
return codecs.open(fn, mode + 'b', 'utf-8',
297
raise BzrError("invalid controlfile mode %r" % mode)
301
def _make_control(self):
302
from bzrlib.inventory import Inventory
303
from bzrlib.xml import pack_xml
305
os.mkdir(self.controlfilename([]))
306
self.controlfile('README', 'w').write(
307
"This is a Bazaar-NG control directory.\n"
308
"Do not change any files in this directory.\n")
309
self.controlfile('branch-format', 'w').write(BZR_BRANCH_FORMAT)
310
for d in ('text-store', 'inventory-store', 'revision-store'):
311
os.mkdir(self.controlfilename(d))
312
for f in ('revision-history', 'merged-patches',
313
'pending-merged-patches', 'branch-name',
316
self.controlfile(f, 'w').write('')
317
mutter('created control directory in ' + self.base)
319
pack_xml(Inventory(gen_root_id()), self.controlfile('inventory','w'))
322
def _check_format(self):
323
"""Check this branch format is supported.
325
The current tool only supports the current unstable format.
327
In the future, we might need different in-memory Branch
328
classes to support downlevel branches. But not yet.
330
# This ignores newlines so that we can open branches created
331
# on Windows from Linux and so on. I think it might be better
332
# to always make all internal files in unix format.
333
fmt = self.controlfile('branch-format', 'r').read()
334
fmt.replace('\r\n', '')
335
if fmt != BZR_BRANCH_FORMAT:
336
raise BzrError('sorry, branch format %r not supported' % fmt,
337
['use a different bzr version',
338
'or remove the .bzr directory and "bzr init" again'])
340
def get_root_id(self):
341
"""Return the id of this branches root"""
342
inv = self.read_working_inventory()
343
return inv.root.file_id
345
def set_root_id(self, file_id):
346
inv = self.read_working_inventory()
347
orig_root_id = inv.root.file_id
348
del inv._byid[inv.root.file_id]
349
inv.root.file_id = file_id
350
inv._byid[inv.root.file_id] = inv.root
353
if entry.parent_id in (None, orig_root_id):
354
entry.parent_id = inv.root.file_id
355
self._write_inventory(inv)
357
def read_working_inventory(self):
358
"""Read the working inventory."""
359
from bzrlib.inventory import Inventory
360
from bzrlib.xml import unpack_xml
361
from time import time
365
# ElementTree does its own conversion from UTF-8, so open in
367
inv = unpack_xml(Inventory,
368
self.controlfile('inventory', 'rb'))
369
mutter("loaded inventory of %d items in %f"
370
% (len(inv), time() - before))
376
def _write_inventory(self, inv):
377
"""Update the working inventory.
379
That is to say, the inventory describing changes underway, that
380
will be committed to the next revision.
382
from bzrlib.atomicfile import AtomicFile
383
from bzrlib.xml import pack_xml
387
f = AtomicFile(self.controlfilename('inventory'), 'wb')
396
mutter('wrote working inventory')
399
inventory = property(read_working_inventory, _write_inventory, None,
400
"""Inventory for the working copy.""")
403
def add(self, files, verbose=False, ids=None):
404
"""Make files versioned.
406
Note that the command line normally calls smart_add instead.
408
This puts the files in the Added state, so that they will be
409
recorded by the next commit.
412
List of paths to add, relative to the base of the tree.
415
If set, use these instead of automatically generated ids.
416
Must be the same length as the list of files, but may
417
contain None for ids that are to be autogenerated.
419
TODO: Perhaps have an option to add the ids even if the files do
422
TODO: Perhaps return the ids of the files? But then again it
423
is easy to retrieve them if they're needed.
425
TODO: Adding a directory should optionally recurse down and
426
add all non-ignored children. Perhaps do that in a
429
# TODO: Re-adding a file that is removed in the working copy
430
# should probably put it back with the previous ID.
431
if isinstance(files, basestring):
432
assert(ids is None or isinstance(ids, basestring))
438
ids = [None] * len(files)
440
assert(len(ids) == len(files))
444
inv = self.read_working_inventory()
445
for f,file_id in zip(files, ids):
446
if is_control_file(f):
447
raise BzrError("cannot add control file %s" % quotefn(f))
452
raise BzrError("cannot add top-level %r" % f)
454
fullpath = os.path.normpath(self.abspath(f))
457
kind = file_kind(fullpath)
459
# maybe something better?
460
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
462
if kind != 'file' and kind != 'directory':
463
raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
466
file_id = gen_file_id(f)
467
inv.add_path(f, kind=kind, file_id=file_id)
470
print 'added', quotefn(f)
472
mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
474
self._write_inventory(inv)
479
def print_file(self, file, revno):
480
"""Print `file` to stdout."""
483
tree = self.revision_tree(self.lookup_revision(revno))
484
# use inventory as it was in that revision
485
file_id = tree.inventory.path2id(file)
487
raise BzrError("%r is not present in revision %s" % (file, revno))
488
tree.print_file(file_id)
493
def remove(self, files, verbose=False):
494
"""Mark nominated files for removal from the inventory.
496
This does not remove their text. This does not run on
498
TODO: Refuse to remove modified files unless --force is given?
500
TODO: Do something useful with directories.
502
TODO: Should this remove the text or not? Tough call; not
503
removing may be useful and the user can just use use rm, and
504
is the opposite of add. Removing it is consistent with most
505
other tools. Maybe an option.
507
## TODO: Normalize names
508
## TODO: Remove nested loops; better scalability
509
if isinstance(files, basestring):
515
tree = self.working_tree()
518
# do this before any modifications
522
raise BzrError("cannot remove unversioned file %s" % quotefn(f))
523
mutter("remove inventory entry %s {%s}" % (quotefn(f), fid))
525
# having remove it, it must be either ignored or unknown
526
if tree.is_ignored(f):
530
show_status(new_status, inv[fid].kind, quotefn(f))
533
self._write_inventory(inv)
538
# FIXME: this doesn't need to be a branch method
539
def set_inventory(self, new_inventory_list):
540
from bzrlib.inventory import Inventory, InventoryEntry
541
inv = Inventory(self.get_root_id())
542
for path, file_id, parent, kind in new_inventory_list:
543
name = os.path.basename(path)
546
inv.add(InventoryEntry(file_id, name, kind, parent))
547
self._write_inventory(inv)
551
"""Return all unknown files.
553
These are files in the working directory that are not versioned or
554
control files or ignored.
556
>>> b = ScratchBranch(files=['foo', 'foo~'])
557
>>> list(b.unknowns())
560
>>> list(b.unknowns())
563
>>> list(b.unknowns())
566
return self.working_tree().unknowns()
569
def append_revision(self, *revision_ids):
570
from bzrlib.atomicfile import AtomicFile
572
for revision_id in revision_ids:
573
mutter("add {%s} to revision-history" % revision_id)
575
rev_history = self.revision_history()
576
rev_history.extend(revision_ids)
578
f = AtomicFile(self.controlfilename('revision-history'))
580
for rev_id in rev_history:
587
def get_revision_xml(self, revision_id):
588
"""Return XML file object for revision object."""
589
if not revision_id or not isinstance(revision_id, basestring):
590
raise InvalidRevisionId(revision_id)
595
return self.revision_store[revision_id]
597
raise bzrlib.errors.NoSuchRevision(revision_id)
602
def get_revision(self, revision_id):
603
"""Return the Revision object for a named revision"""
604
xml_file = self.get_revision_xml(revision_id)
607
r = unpack_xml(Revision, xml_file)
608
except SyntaxError, e:
609
raise bzrlib.errors.BzrError('failed to unpack revision_xml',
613
assert r.revision_id == revision_id
197
"""Return absolute filename for something in the branch
199
XXX: Robert Collins 20051017 what is this used for? why is it a branch
200
method and not a tree method.
202
raise NotImplementedError(self.abspath)
204
def bind(self, other):
205
"""Bind the local branch the other branch.
207
:param other: The branch to bind to
210
raise errors.UpgradeRequired(self.base)
213
def fetch(self, from_branch, last_revision=None, pb=None):
214
"""Copy revisions from from_branch into this branch.
216
:param from_branch: Where to copy from.
217
:param last_revision: What revision to stop at (None for at the end
219
:param pb: An optional progress bar to use.
221
Returns the copied revision count and the failed revisions in a tuple:
224
if self.base == from_branch.base:
227
nested_pb = ui.ui_factory.nested_progress_bar()
232
from_branch.lock_read()
234
if last_revision is None:
235
pb.update('get source history')
236
last_revision = from_branch.last_revision()
237
if last_revision is None:
238
last_revision = _mod_revision.NULL_REVISION
239
return self.repository.fetch(from_branch.repository,
240
revision_id=last_revision,
243
if nested_pb is not None:
247
def get_bound_location(self):
248
"""Return the URL of the branch we are bound to.
250
Older format branches cannot bind, please be sure to use a metadir
255
def get_old_bound_location(self):
256
"""Return the URL of the branch we used to be bound to
258
raise errors.UpgradeRequired(self.base)
260
def get_commit_builder(self, parents, config=None, timestamp=None,
261
timezone=None, committer=None, revprops=None,
263
"""Obtain a CommitBuilder for this branch.
265
:param parents: Revision ids of the parents of the new revision.
266
:param config: Optional configuration to use.
267
:param timestamp: Optional timestamp recorded for commit.
268
:param timezone: Optional timezone for timestamp.
269
:param committer: Optional committer to set for commit.
270
:param revprops: Optional dictionary of revision properties.
271
:param revision_id: Optional revision id.
275
config = self.get_config()
277
return self.repository.get_commit_builder(self, parents, config,
278
timestamp, timezone, committer, revprops, revision_id)
280
def get_master_branch(self):
281
"""Return the branch we are bound to.
283
:return: Either a Branch, or None
617
287
def get_revision_delta(self, revno):
618
288
"""Return the delta for one revision.
1125
441
Note that to_name is only the last component of the new name;
1126
442
this doesn't change the directory.
1130
## TODO: Option to move IDs only
1131
assert not isinstance(from_paths, basestring)
1132
tree = self.working_tree()
1133
inv = tree.inventory
1134
to_abs = self.abspath(to_name)
1135
if not isdir(to_abs):
1136
raise BzrError("destination %r is not a directory" % to_abs)
1137
if not tree.has_filename(to_name):
1138
raise BzrError("destination %r not in working directory" % to_abs)
1139
to_dir_id = inv.path2id(to_name)
1140
if to_dir_id == None and to_name != '':
1141
raise BzrError("destination %r is not a versioned directory" % to_name)
1142
to_dir_ie = inv[to_dir_id]
1143
if to_dir_ie.kind not in ('directory', 'root_directory'):
1144
raise BzrError("destination %r is not a directory" % to_abs)
1146
to_idpath = inv.get_idpath(to_dir_id)
1148
for f in from_paths:
1149
if not tree.has_filename(f):
1150
raise BzrError("%r does not exist in working tree" % f)
1151
f_id = inv.path2id(f)
1153
raise BzrError("%r is not versioned" % f)
1154
name_tail = splitpath(f)[-1]
1155
dest_path = appendpath(to_name, name_tail)
1156
if tree.has_filename(dest_path):
1157
raise BzrError("destination %r already exists" % dest_path)
1158
if f_id in to_idpath:
1159
raise BzrError("can't move %r to a subdirectory of itself" % f)
1161
# OK, so there's a race here, it's possible that someone will
1162
# create a file in this interval and then the rename might be
1163
# left half-done. But we should have caught most problems.
1165
for f in from_paths:
1166
name_tail = splitpath(f)[-1]
1167
dest_path = appendpath(to_name, name_tail)
1168
print "%s => %s" % (f, dest_path)
1169
inv.rename(inv.path2id(f), to_dir_id, name_tail)
1171
os.rename(self.abspath(f), self.abspath(dest_path))
1173
raise BzrError("failed to rename %r to %r: %s" % (f, dest_path, e[1]),
1174
["rename rolled back"])
1176
self._write_inventory(inv)
1181
def revert(self, filenames, old_tree=None, backups=True):
1182
"""Restore selected files to the versions from a previous tree.
1185
If true (default) backups are made of files before
1188
from bzrlib.errors import NotVersionedError, BzrError
1189
from bzrlib.atomicfile import AtomicFile
1190
from bzrlib.osutils import backup_file
1192
inv = self.read_working_inventory()
1193
if old_tree is None:
1194
old_tree = self.basis_tree()
1195
old_inv = old_tree.inventory
1198
for fn in filenames:
1199
file_id = inv.path2id(fn)
1201
raise NotVersionedError("not a versioned file", fn)
1202
if not old_inv.has_id(file_id):
1203
raise BzrError("file not present in old tree", fn, file_id)
1204
nids.append((fn, file_id))
1206
# TODO: Rename back if it was previously at a different location
1208
# TODO: If given a directory, restore the entire contents from
1209
# the previous version.
1211
# TODO: Make a backup to a temporary file.
1213
# TODO: If the file previously didn't exist, delete it?
1214
for fn, file_id in nids:
1217
f = AtomicFile(fn, 'wb')
1219
f.write(old_tree.get_file(file_id).read())
1225
def pending_merges(self):
1226
"""Return a list of pending merges.
1228
These are revisions that have been merged into the working
1229
directory but not yet committed.
1231
cfn = self.controlfilename('pending-merges')
1232
if not os.path.exists(cfn):
1235
for l in self.controlfile('pending-merges', 'r').readlines():
1236
p.append(l.rstrip('\n'))
1240
def add_pending_merge(self, revision_id):
1241
from bzrlib.revision import validate_revision_id
1243
validate_revision_id(revision_id)
1245
p = self.pending_merges()
1246
if revision_id in p:
1248
p.append(revision_id)
1249
self.set_pending_merges(p)
1252
def set_pending_merges(self, rev_list):
1253
from bzrlib.atomicfile import AtomicFile
1256
f = AtomicFile(self.controlfilename('pending-merges'))
1268
class ScratchBranch(Branch):
1269
"""Special test class: a branch that cleans up after itself.
1271
>>> b = ScratchBranch()
1279
def __init__(self, files=[], dirs=[], base=None):
1280
"""Make a test branch.
1282
This creates a temporary directory and runs init-tree in it.
1284
If any files are listed, they are created in the working copy.
1286
from tempfile import mkdtemp
1291
Branch.__init__(self, base, init=init)
1293
os.mkdir(self.abspath(d))
1296
file(os.path.join(self.base, f), 'w').write('content of %s' % f)
1301
>>> orig = ScratchBranch(files=["file1", "file2"])
1302
>>> clone = orig.clone()
1303
>>> os.path.samefile(orig.base, clone.base)
1305
>>> os.path.isfile(os.path.join(clone.base, "file1"))
1308
from shutil import copytree
1309
from tempfile import mkdtemp
1312
copytree(self.base, base, symlinks=True)
1313
return ScratchBranch(base=base)
1319
"""Destroy the test branch, removing the scratch directory."""
1320
from shutil import rmtree
1323
mutter("delete ScratchBranch %s" % self.base)
1326
# Work around for shutil.rmtree failing on Windows when
1327
# readonly files are encountered
1328
mutter("hit exception in destroying ScratchBranch: %s" % e)
1329
for root, dirs, files in os.walk(self.base, topdown=False):
1331
os.chmod(os.path.join(root, name), 0700)
1337
######################################################################
1341
def is_control_file(filename):
1342
## FIXME: better check
1343
filename = os.path.normpath(filename)
1344
while filename != '':
1345
head, tail = os.path.split(filename)
1346
## mutter('check %r for control file' % ((head, tail), ))
1347
if tail == bzrlib.BZRDIR:
444
This returns a list of (from_path, to_path) pairs for each
447
raise NotImplementedError(self.move)
449
def get_parent(self):
450
"""Return the parent location of the branch.
452
This is the default location for push/pull/missing. The usual
453
pattern is that the user can override it by specifying a
456
raise NotImplementedError(self.get_parent)
458
def _set_config_location(self, name, url, config=None,
459
make_relative=False):
461
config = self.get_config()
465
url = urlutils.relative_url(self.base, url)
466
config.set_user_option(name, url)
468
def _get_config_location(self, name, config=None):
470
config = self.get_config()
471
location = config.get_user_option(name)
476
def get_submit_branch(self):
477
"""Return the submit location of the branch.
479
This is the default location for bundle. The usual
480
pattern is that the user can override it by specifying a
483
return self.get_config().get_user_option('submit_branch')
485
def set_submit_branch(self, location):
486
"""Return the submit location of the branch.
488
This is the default location for bundle. The usual
489
pattern is that the user can override it by specifying a
492
self.get_config().set_user_option('submit_branch', location)
494
def get_public_branch(self):
495
"""Return the public location of the branch.
497
This is is used by merge directives.
499
return self._get_config_location('public_branch')
501
def set_public_branch(self, location):
502
"""Return the submit location of the branch.
504
This is the default location for bundle. The usual
505
pattern is that the user can override it by specifying a
508
self._set_config_location('public_branch', location)
510
def get_push_location(self):
511
"""Return the None or the location to push this branch to."""
512
raise NotImplementedError(self.get_push_location)
514
def set_push_location(self, location):
515
"""Set a new push location for this branch."""
516
raise NotImplementedError(self.set_push_location)
518
def set_parent(self, url):
519
raise NotImplementedError(self.set_parent)
523
"""Synchronise this branch with the master branch if any.
525
:return: None or the last_revision pivoted out during the update.
529
def check_revno(self, revno):
531
Check whether a revno corresponds to any revision.
532
Zero (the NULL revision) is considered valid.
535
self.check_real_revno(revno)
537
def check_real_revno(self, revno):
539
Check whether a revno corresponds to a real revision.
540
Zero (the NULL revision) is considered invalid
542
if revno < 1 or revno > self.revno():
543
raise InvalidRevisionNumber(revno)
546
def clone(self, *args, **kwargs):
547
"""Clone this branch into to_bzrdir preserving all semantic values.
549
revision_id: if not None, the revision history in the new branch will
550
be truncated to end with revision_id.
552
# for API compatibility, until 0.8 releases we provide the old api:
553
# def clone(self, to_location, revision=None, basis_branch=None, to_branch_format=None):
554
# after 0.8 releases, the *args and **kwargs should be changed:
555
# def clone(self, to_bzrdir, revision_id=None):
556
if (kwargs.get('to_location', None) or
557
kwargs.get('revision', None) or
558
kwargs.get('basis_branch', None) or
559
(len(args) and isinstance(args[0], basestring))):
560
# backwards compatibility api:
561
warn("Branch.clone() has been deprecated for BzrDir.clone() from"
562
" bzrlib 0.8.", DeprecationWarning, stacklevel=3)
565
basis_branch = args[2]
567
basis_branch = kwargs.get('basis_branch', None)
569
basis = basis_branch.bzrdir
574
revision_id = args[1]
576
revision_id = kwargs.get('revision', None)
581
# no default to raise if not provided.
582
url = kwargs.get('to_location')
583
return self.bzrdir.clone(url,
584
revision_id=revision_id,
585
basis=basis).open_branch()
587
# generate args by hand
589
revision_id = args[1]
591
revision_id = kwargs.get('revision_id', None)
595
# no default to raise if not provided.
596
to_bzrdir = kwargs.get('to_bzrdir')
597
result = self._format.initialize(to_bzrdir)
598
self.copy_content_into(result, revision_id=revision_id)
602
def sprout(self, to_bzrdir, revision_id=None):
603
"""Create a new line of development from the branch, into to_bzrdir.
605
revision_id: if not None, the revision history in the new branch will
606
be truncated to end with revision_id.
608
result = self._format.initialize(to_bzrdir)
609
self.copy_content_into(result, revision_id=revision_id)
610
result.set_parent(self.bzrdir.root_transport.base)
613
def _synchronize_history(self, destination, revision_id):
614
"""Synchronize last revision and revision history between branches.
616
This version is most efficient when the destination is also a
617
BzrBranch5, but works for BzrBranch6 as long as the revision
618
history is the true lefthand parent history, and all of the revisions
619
are in the destination's repository. If not, set_revision_history
622
:param destination: The branch to copy the history into
623
:param revision_id: The revision-id to truncate history at. May
624
be None to copy complete history.
626
new_history = self.revision_history()
627
if revision_id is not None:
628
revision_id = osutils.safe_revision_id(revision_id)
630
new_history = new_history[:new_history.index(revision_id) + 1]
632
rev = self.repository.get_revision(revision_id)
633
new_history = rev.get_history(self.repository)[1:]
634
destination.set_revision_history(new_history)
637
def copy_content_into(self, destination, revision_id=None):
638
"""Copy the content of self into destination.
640
revision_id: if not None, the revision history in the new branch will
641
be truncated to end with revision_id.
643
self._synchronize_history(destination, revision_id)
645
parent = self.get_parent()
646
except errors.InaccessibleParent, e:
647
mutter('parent was not accessible to copy: %s', e)
650
destination.set_parent(parent)
651
self.tags.merge_to(destination.tags)
655
"""Check consistency of the branch.
657
In particular this checks that revisions given in the revision-history
658
do actually match up in the revision graph, and that they're all
659
present in the repository.
661
Callers will typically also want to check the repository.
663
:return: A BranchCheckResult.
665
mainline_parent_id = None
666
for revision_id in self.revision_history():
668
revision = self.repository.get_revision(revision_id)
669
except errors.NoSuchRevision, e:
670
raise errors.BzrCheckError("mainline revision {%s} not in repository"
672
# In general the first entry on the revision history has no parents.
673
# But it's not illegal for it to have parents listed; this can happen
674
# in imports from Arch when the parents weren't reachable.
675
if mainline_parent_id is not None:
676
if mainline_parent_id not in revision.parent_ids:
677
raise errors.BzrCheckError("previous revision {%s} not listed among "
679
% (mainline_parent_id, revision_id))
680
mainline_parent_id = revision_id
681
return BranchCheckResult(self)
683
def _get_checkout_format(self):
684
"""Return the most suitable metadir for a checkout of this branch.
685
Weaves are used if this branch's repostory uses weaves.
687
if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
688
from bzrlib.repofmt import weaverepo
689
format = bzrdir.BzrDirMetaFormat1()
690
format.repository_format = weaverepo.RepositoryFormat7()
692
format = self.repository.bzrdir.checkout_metadir()
693
format.branch_format = self._format
696
def create_checkout(self, to_location, revision_id=None,
698
"""Create a checkout of a branch.
700
:param to_location: The url to produce the checkout at
701
:param revision_id: The revision to check out
702
:param lightweight: If True, produce a lightweight checkout, otherwise,
703
produce a bound branch (heavyweight checkout)
704
:return: The tree of the created checkout
706
t = transport.get_transport(to_location)
709
except errors.FileExists:
712
format = self._get_checkout_format()
713
checkout = format.initialize_on_transport(t)
714
BranchReferenceFormat().initialize(checkout, self)
716
format = self._get_checkout_format()
717
checkout_branch = bzrdir.BzrDir.create_branch_convenience(
718
to_location, force_new_tree=False, format=format)
719
checkout = checkout_branch.bzrdir
720
checkout_branch.bind(self)
721
# pull up to the specified revision_id to set the initial
722
# branch tip correctly, and seed it with history.
723
checkout_branch.pull(self, stop_revision=revision_id)
724
tree = checkout.create_workingtree(revision_id)
725
basis_tree = tree.basis_tree()
726
basis_tree.lock_read()
728
for path, file_id in basis_tree.iter_references():
729
reference_parent = self.reference_parent(file_id, path)
730
reference_parent.create_checkout(tree.abspath(path),
731
basis_tree.get_reference_revision(file_id, path),
737
def reference_parent(self, file_id, path):
738
"""Return the parent branch for a tree-reference file_id
739
:param file_id: The file_id of the tree reference
740
:param path: The path of the file_id in the tree
741
:return: A branch associated with the file_id
743
# FIXME should provide multiple branches, based on config
744
return Branch.open(self.bzrdir.root_transport.clone(path).base)
746
def supports_tags(self):
747
return self._format.supports_tags()
750
class BranchFormat(object):
751
"""An encapsulation of the initialization and open routines for a format.
753
Formats provide three things:
754
* An initialization routine,
758
Formats are placed in an dict by their format string for reference
759
during branch opening. Its not required that these be instances, they
760
can be classes themselves with class methods - it simply depends on
761
whether state is needed for a given format or not.
763
Once a format is deprecated, just deprecate the initialize and open
764
methods on the format class. Do not deprecate the object, as the
765
object will be created every time regardless.
768
_default_format = None
769
"""The default format used for new branches."""
772
"""The known formats."""
775
def find_format(klass, a_bzrdir):
776
"""Return the format for the branch object in a_bzrdir."""
778
transport = a_bzrdir.get_branch_transport(None)
779
format_string = transport.get("format").read()
780
return klass._formats[format_string]
782
raise NotBranchError(path=transport.base)
784
raise errors.UnknownFormatError(format=format_string)
787
def get_default_format(klass):
788
"""Return the current default format."""
789
return klass._default_format
791
def get_format_string(self):
792
"""Return the ASCII format string that identifies this format."""
793
raise NotImplementedError(self.get_format_string)
795
def get_format_description(self):
796
"""Return the short format description for this format."""
797
raise NotImplementedError(self.get_format_description)
799
def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
801
"""Initialize a branch in a bzrdir, with specified files
803
:param a_bzrdir: The bzrdir to initialize the branch in
804
:param utf8_files: The files to create as a list of
805
(filename, content) tuples
806
:param set_format: If True, set the format with
807
self.get_format_string. (BzrBranch4 has its format set
809
:return: a branch in this format
811
mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
812
branch_transport = a_bzrdir.get_branch_transport(self)
814
'metadir': ('lock', lockdir.LockDir),
815
'branch4': ('branch-lock', lockable_files.TransportLock),
817
lock_name, lock_class = lock_map[lock_type]
818
control_files = lockable_files.LockableFiles(branch_transport,
819
lock_name, lock_class)
820
control_files.create_lock()
821
control_files.lock_write()
823
control_files.put_utf8('format', self.get_format_string())
825
for file, content in utf8_files:
826
control_files.put_utf8(file, content)
828
control_files.unlock()
829
return self.open(a_bzrdir, _found=True)
831
def initialize(self, a_bzrdir):
832
"""Create a branch of this format in a_bzrdir."""
833
raise NotImplementedError(self.initialize)
835
def is_supported(self):
836
"""Is this format supported?
838
Supported formats can be initialized and opened.
839
Unsupported formats may not support initialization or committing or
840
some other features depending on the reason for not being supported.
844
def open(self, a_bzrdir, _found=False):
845
"""Return the branch object for a_bzrdir
847
_found is a private parameter, do not use it. It is used to indicate
848
if format probing has already be done.
850
raise NotImplementedError(self.open)
853
def register_format(klass, format):
854
klass._formats[format.get_format_string()] = format
857
def set_default_format(klass, format):
858
klass._default_format = format
861
def unregister_format(klass, format):
862
assert klass._formats[format.get_format_string()] is format
863
del klass._formats[format.get_format_string()]
866
return self.get_format_string().rstrip()
868
def supports_tags(self):
869
"""True if this format supports tags stored in the branch"""
870
return False # by default
872
# XXX: Probably doesn't really belong here -- mbp 20070212
873
def _initialize_control_files(self, a_bzrdir, utf8_files, lock_filename,
875
branch_transport = a_bzrdir.get_branch_transport(self)
876
control_files = lockable_files.LockableFiles(branch_transport,
877
lock_filename, lock_class)
878
control_files.create_lock()
879
control_files.lock_write()
881
for filename, content in utf8_files:
882
control_files.put_utf8(filename, content)
884
control_files.unlock()
887
class BranchHooks(dict):
888
"""A dictionary mapping hook name to a list of callables for branch hooks.
890
e.g. ['set_rh'] Is the list of items to be called when the
891
set_revision_history function is invoked.
895
"""Create the default hooks.
897
These are all empty initially, because by default nothing should get
901
# Introduced in 0.15:
902
# invoked whenever the revision history has been set
903
# with set_revision_history. The api signature is
904
# (branch, revision_history), and the branch will
907
# invoked after a push operation completes.
908
# the api signature is
910
# containing the members
911
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
912
# where local is the local branch or None, master is the target
913
# master branch, and the rest should be self explanatory. The source
914
# is read locked and the target branches write locked. Source will
915
# be the local low-latency branch.
916
self['post_push'] = []
917
# invoked after a pull operation completes.
918
# the api signature is
920
# containing the members
921
# (source, local, master, old_revno, old_revid, new_revno, new_revid)
922
# where local is the local branch or None, master is the target
923
# master branch, and the rest should be self explanatory. The source
924
# is read locked and the target branches write locked. The local
925
# branch is the low-latency branch.
926
self['post_pull'] = []
927
# invoked after a commit operation completes.
928
# the api signature is
929
# (local, master, old_revno, old_revid, new_revno, new_revid)
930
# old_revid is NULL_REVISION for the first commit to a branch.
931
self['post_commit'] = []
932
# invoked after a uncommit operation completes.
933
# the api signature is
934
# (local, master, old_revno, old_revid, new_revno, new_revid) where
935
# local is the local branch or None, master is the target branch,
936
# and an empty branch recieves new_revno of 0, new_revid of None.
937
self['post_uncommit'] = []
939
def install_hook(self, hook_name, a_callable):
940
"""Install a_callable in to the hook hook_name.
942
:param hook_name: A hook name. See the __init__ method of BranchHooks
943
for the complete list of hooks.
944
:param a_callable: The callable to be invoked when the hook triggers.
945
The exact signature will depend on the hook - see the __init__
946
method of BranchHooks for details on each hook.
949
self[hook_name].append(a_callable)
951
raise errors.UnknownHook('branch', hook_name)
954
# install the default hooks into the Branch class.
955
Branch.hooks = BranchHooks()
958
class BzrBranchFormat4(BranchFormat):
959
"""Bzr branch format 4.
962
- a revision-history file.
963
- a branch-lock lock file [ to be shared with the bzrdir ]
966
def get_format_description(self):
967
"""See BranchFormat.get_format_description()."""
968
return "Branch format 4"
970
def initialize(self, a_bzrdir):
971
"""Create a branch of this format in a_bzrdir."""
972
utf8_files = [('revision-history', ''),
975
return self._initialize_helper(a_bzrdir, utf8_files,
976
lock_type='branch4', set_format=False)
979
super(BzrBranchFormat4, self).__init__()
980
self._matchingbzrdir = bzrdir.BzrDirFormat6()
982
def open(self, a_bzrdir, _found=False):
983
"""Return the branch object for a_bzrdir
985
_found is a private parameter, do not use it. It is used to indicate
986
if format probing has already be done.
989
# we are being called directly and must probe.
990
raise NotImplementedError
991
return BzrBranch(_format=self,
992
_control_files=a_bzrdir._control_files,
994
_repository=a_bzrdir.open_repository())
997
return "Bazaar-NG branch format 4"
1000
class BzrBranchFormat5(BranchFormat):
1001
"""Bzr branch format 5.
1004
- a revision-history file.
1006
- a lock dir guarding the branch itself
1007
- all of this stored in a branch/ subdirectory
1008
- works with shared repositories.
1010
This format is new in bzr 0.8.
1013
def get_format_string(self):
1014
"""See BranchFormat.get_format_string()."""
1015
return "Bazaar-NG branch format 5\n"
1017
def get_format_description(self):
1018
"""See BranchFormat.get_format_description()."""
1019
return "Branch format 5"
1021
def initialize(self, a_bzrdir):
1022
"""Create a branch of this format in a_bzrdir."""
1023
utf8_files = [('revision-history', ''),
1024
('branch-name', ''),
1026
return self._initialize_helper(a_bzrdir, utf8_files)
1029
super(BzrBranchFormat5, self).__init__()
1030
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1032
def open(self, a_bzrdir, _found=False):
1033
"""Return the branch object for a_bzrdir
1035
_found is a private parameter, do not use it. It is used to indicate
1036
if format probing has already be done.
1039
format = BranchFormat.find_format(a_bzrdir)
1040
assert format.__class__ == self.__class__
1041
transport = a_bzrdir.get_branch_transport(None)
1042
control_files = lockable_files.LockableFiles(transport, 'lock',
1044
return BzrBranch5(_format=self,
1045
_control_files=control_files,
1047
_repository=a_bzrdir.find_repository())
1050
class BzrBranchFormat6(BzrBranchFormat5):
1051
"""Branch format with last-revision
1053
Unlike previous formats, this has no explicit revision history. Instead,
1054
this just stores the last-revision, and the left-hand history leading
1055
up to there is the history.
1057
This format was introduced in bzr 0.15
1060
def get_format_string(self):
1061
"""See BranchFormat.get_format_string()."""
1062
return "Bazaar Branch Format 6 (bzr 0.15)\n"
1064
def get_format_description(self):
1065
"""See BranchFormat.get_format_description()."""
1066
return "Branch format 6"
1068
def initialize(self, a_bzrdir):
1069
"""Create a branch of this format in a_bzrdir."""
1070
utf8_files = [('last-revision', '0 null:\n'),
1071
('branch-name', ''),
1072
('branch.conf', ''),
1075
return self._initialize_helper(a_bzrdir, utf8_files)
1077
def open(self, a_bzrdir, _found=False):
1078
"""Return the branch object for a_bzrdir
1080
_found is a private parameter, do not use it. It is used to indicate
1081
if format probing has already be done.
1084
format = BranchFormat.find_format(a_bzrdir)
1085
assert format.__class__ == self.__class__
1086
transport = a_bzrdir.get_branch_transport(None)
1087
control_files = lockable_files.LockableFiles(transport, 'lock',
1089
return BzrBranch6(_format=self,
1090
_control_files=control_files,
1092
_repository=a_bzrdir.find_repository())
1094
def supports_tags(self):
1098
class BranchReferenceFormat(BranchFormat):
1099
"""Bzr branch reference format.
1101
Branch references are used in implementing checkouts, they
1102
act as an alias to the real branch which is at some other url.
1109
def get_format_string(self):
1110
"""See BranchFormat.get_format_string()."""
1111
return "Bazaar-NG Branch Reference Format 1\n"
1113
def get_format_description(self):
1114
"""See BranchFormat.get_format_description()."""
1115
return "Checkout reference format 1"
1117
def initialize(self, a_bzrdir, target_branch=None):
1118
"""Create a branch of this format in a_bzrdir."""
1119
if target_branch is None:
1120
# this format does not implement branch itself, thus the implicit
1121
# creation contract must see it as uninitializable
1122
raise errors.UninitializableFormat(self)
1123
mutter('creating branch reference in %s', a_bzrdir.transport.base)
1124
branch_transport = a_bzrdir.get_branch_transport(self)
1125
branch_transport.put_bytes('location',
1126
target_branch.bzrdir.root_transport.base)
1127
branch_transport.put_bytes('format', self.get_format_string())
1128
return self.open(a_bzrdir, _found=True)
1131
super(BranchReferenceFormat, self).__init__()
1132
self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1134
def _make_reference_clone_function(format, a_branch):
1135
"""Create a clone() routine for a branch dynamically."""
1136
def clone(to_bzrdir, revision_id=None):
1137
"""See Branch.clone()."""
1138
return format.initialize(to_bzrdir, a_branch)
1139
# cannot obey revision_id limits when cloning a reference ...
1140
# FIXME RBC 20060210 either nuke revision_id for clone, or
1141
# emit some sort of warning/error to the caller ?!
1144
def open(self, a_bzrdir, _found=False):
1145
"""Return the branch that the branch reference in a_bzrdir points at.
1147
_found is a private parameter, do not use it. It is used to indicate
1148
if format probing has already be done.
1151
format = BranchFormat.find_format(a_bzrdir)
1152
assert format.__class__ == self.__class__
1153
transport = a_bzrdir.get_branch_transport(None)
1154
real_bzrdir = bzrdir.BzrDir.open(transport.get('location').read())
1155
result = real_bzrdir.open_branch()
1156
# this changes the behaviour of result.clone to create a new reference
1157
# rather than a copy of the content of the branch.
1158
# I did not use a proxy object because that needs much more extensive
1159
# testing, and we are only changing one behaviour at the moment.
1160
# If we decide to alter more behaviours - i.e. the implicit nickname
1161
# then this should be refactored to introduce a tested proxy branch
1162
# and a subclass of that for use in overriding clone() and ....
1164
result.clone = self._make_reference_clone_function(result)
1168
# formats which have no format string are not discoverable
1169
# and not independently creatable, so are not registered.
1170
__default_format = BzrBranchFormat5()
1171
BranchFormat.register_format(__default_format)
1172
BranchFormat.register_format(BranchReferenceFormat())
1173
BranchFormat.register_format(BzrBranchFormat6())
1174
BranchFormat.set_default_format(__default_format)
1175
_legacy_formats = [BzrBranchFormat4(),
1178
class BzrBranch(Branch):
1179
"""A branch stored in the actual filesystem.
1181
Note that it's "local" in the context of the filesystem; it doesn't
1182
really matter if it's on an nfs/smb/afs/coda/... share, as long as
1183
it's writable, and can be accessed via the normal filesystem API.
1186
def __init__(self, transport=DEPRECATED_PARAMETER, init=DEPRECATED_PARAMETER,
1187
relax_version_check=DEPRECATED_PARAMETER, _format=None,
1188
_control_files=None, a_bzrdir=None, _repository=None):
1189
"""Create new branch object at a particular location.
1191
transport -- A Transport object, defining how to access files.
1193
init -- If True, create new control files in a previously
1194
unversioned directory. If False, the branch must already
1197
relax_version_check -- If true, the usual check for the branch
1198
version is not applied. This is intended only for
1199
upgrade/recovery type use; it's not guaranteed that
1200
all operations will work on old format branches.
1202
Branch.__init__(self)
1203
if a_bzrdir is None:
1204
self.bzrdir = bzrdir.BzrDir.open(transport.base)
1206
self.bzrdir = a_bzrdir
1207
# self._transport used to point to the directory containing the
1208
# control directory, but was not used - now it's just the transport
1209
# for the branch control files. mbp 20070212
1210
self._base = self.bzrdir.transport.clone('..').base
1211
self._format = _format
1212
if _control_files is None:
1213
raise ValueError('BzrBranch _control_files is None')
1214
self.control_files = _control_files
1215
self._transport = _control_files._transport
1216
if deprecated_passed(init):
1217
warn("BzrBranch.__init__(..., init=XXX): The init parameter is "
1218
"deprecated as of bzr 0.8. Please use Branch.create().",
1222
# this is slower than before deprecation, oh well never mind.
1223
# -> its deprecated.
1224
self._initialize(transport.base)
1225
self._check_format(_format)
1226
if deprecated_passed(relax_version_check):
1227
warn("BzrBranch.__init__(..., relax_version_check=XXX_: The "
1228
"relax_version_check parameter is deprecated as of bzr 0.8. "
1229
"Please use BzrDir.open_downlevel, or a BzrBranchFormat's "
1233
if (not relax_version_check
1234
and not self._format.is_supported()):
1235
raise errors.UnsupportedFormatError(format=fmt)
1236
if deprecated_passed(transport):
1237
warn("BzrBranch.__init__(transport=XXX...): The transport "
1238
"parameter is deprecated as of bzr 0.8. "
1239
"Please use Branch.open, or bzrdir.open_branch().",
1242
self.repository = _repository
1245
return '%s(%r)' % (self.__class__.__name__, self.base)
1249
def _get_base(self):
1250
"""Returns the directory containing the control directory."""
1253
base = property(_get_base, doc="The URL for the root of this branch.")
1255
def _finish_transaction(self):
1256
"""Exit the current transaction."""
1257
return self.control_files._finish_transaction()
1259
def get_transaction(self):
1260
"""Return the current active transaction.
1262
If no transaction is active, this returns a passthrough object
1263
for which all data is immediately flushed and no caching happens.
1265
# this is an explicit function so that we can do tricky stuff
1266
# when the storage in rev_storage is elsewhere.
1267
# we probably need to hook the two 'lock a location' and
1268
# 'have a transaction' together more delicately, so that
1269
# we can have two locks (branch and storage) and one transaction
1270
# ... and finishing the transaction unlocks both, but unlocking
1271
# does not. - RBC 20051121
1272
return self.control_files.get_transaction()
1274
def _set_transaction(self, transaction):
1275
"""Set a new active transaction."""
1276
return self.control_files._set_transaction(transaction)
1278
def abspath(self, name):
1279
"""See Branch.abspath."""
1280
return self.control_files._transport.abspath(name)
1282
def _check_format(self, format):
1283
"""Identify the branch format if needed.
1285
The format is stored as a reference to the format object in
1286
self._format for code that needs to check it later.
1288
The format parameter is either None or the branch format class
1289
used to open this branch.
1291
FIXME: DELETE THIS METHOD when pre 0.8 support is removed.
1294
format = BranchFormat.find_format(self.bzrdir)
1295
self._format = format
1296
mutter("got branch format %s", self._format)
1299
def get_root_id(self):
1300
"""See Branch.get_root_id."""
1301
tree = self.repository.revision_tree(self.last_revision())
1302
return tree.inventory.root.file_id
1304
def is_locked(self):
1305
return self.control_files.is_locked()
1307
def lock_write(self):
1308
self.repository.lock_write()
1310
self.control_files.lock_write()
1312
self.repository.unlock()
1315
def lock_read(self):
1316
self.repository.lock_read()
1318
self.control_files.lock_read()
1320
self.repository.unlock()
1324
# TODO: test for failed two phase locks. This is known broken.
1326
self.control_files.unlock()
1328
self.repository.unlock()
1330
def peek_lock_mode(self):
1331
if self.control_files._lock_count == 0:
1334
return self.control_files._lock_mode
1336
def get_physical_lock_status(self):
1337
return self.control_files.get_physical_lock_status()
1340
def print_file(self, file, revision_id):
1341
"""See Branch.print_file."""
1342
return self.repository.print_file(file, revision_id)
1345
def append_revision(self, *revision_ids):
1346
"""See Branch.append_revision."""
1347
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1348
for revision_id in revision_ids:
1349
_mod_revision.check_not_reserved_id(revision_id)
1350
mutter("add {%s} to revision-history" % revision_id)
1351
rev_history = self.revision_history()
1352
rev_history.extend(revision_ids)
1353
self.set_revision_history(rev_history)
1355
def _write_revision_history(self, history):
1356
"""Factored out of set_revision_history.
1358
This performs the actual writing to disk.
1359
It is intended to be called by BzrBranch5.set_revision_history."""
1360
self.control_files.put_bytes(
1361
'revision-history', '\n'.join(history))
1364
def set_revision_history(self, rev_history):
1365
"""See Branch.set_revision_history."""
1366
rev_history = [osutils.safe_revision_id(r) for r in rev_history]
1367
self._write_revision_history(rev_history)
1368
transaction = self.get_transaction()
1369
history = transaction.map.find_revision_history()
1370
if history is not None:
1371
# update the revision history in the identity map.
1372
history[:] = list(rev_history)
1373
# this call is disabled because revision_history is
1374
# not really an object yet, and the transaction is for objects.
1375
# transaction.register_dirty(history)
1377
transaction.map.add_revision_history(rev_history)
1378
# this call is disabled because revision_history is
1379
# not really an object yet, and the transaction is for objects.
1380
# transaction.register_clean(history)
1381
for hook in Branch.hooks['set_rh']:
1382
hook(self, rev_history)
1385
def set_last_revision_info(self, revno, revision_id):
1386
revision_id = osutils.safe_revision_id(revision_id)
1387
history = self._lefthand_history(revision_id)
1388
assert len(history) == revno, '%d != %d' % (len(history), revno)
1389
self.set_revision_history(history)
1391
def _gen_revision_history(self):
1392
history = self.control_files.get('revision-history').read().split('\n')
1393
if history[-1:] == ['']:
1394
# There shouldn't be a trailing newline, but just in case.
1399
def revision_history(self):
1400
"""See Branch.revision_history."""
1401
transaction = self.get_transaction()
1402
history = transaction.map.find_revision_history()
1403
if history is not None:
1404
# mutter("cache hit for revision-history in %s", self)
1405
return list(history)
1406
history = self._gen_revision_history()
1407
transaction.map.add_revision_history(history)
1408
# this call is disabled because revision_history is
1409
# not really an object yet, and the transaction is for objects.
1410
# transaction.register_clean(history, precious=True)
1411
return list(history)
1413
def _lefthand_history(self, revision_id, last_rev=None,
1415
# stop_revision must be a descendant of last_revision
1416
stop_graph = self.repository.get_revision_graph(revision_id)
1417
if last_rev is not None and last_rev not in stop_graph:
1418
# our previous tip is not merged into stop_revision
1419
raise errors.DivergedBranches(self, other_branch)
1420
# make a new revision history from the graph
1421
current_rev_id = revision_id
1423
while current_rev_id not in (None, _mod_revision.NULL_REVISION):
1424
new_history.append(current_rev_id)
1425
current_rev_id_parents = stop_graph[current_rev_id]
1427
current_rev_id = current_rev_id_parents[0]
1429
current_rev_id = None
1430
new_history.reverse()
1434
def generate_revision_history(self, revision_id, last_rev=None,
1436
"""Create a new revision history that will finish with revision_id.
1438
:param revision_id: the new tip to use.
1439
:param last_rev: The previous last_revision. If not None, then this
1440
must be a ancestory of revision_id, or DivergedBranches is raised.
1441
:param other_branch: The other branch that DivergedBranches should
1442
raise with respect to.
1444
revision_id = osutils.safe_revision_id(revision_id)
1445
self.set_revision_history(self._lefthand_history(revision_id,
1446
last_rev, other_branch))
1449
def update_revisions(self, other, stop_revision=None):
1450
"""See Branch.update_revisions."""
1453
if stop_revision is None:
1454
stop_revision = other.last_revision()
1455
if stop_revision is None:
1456
# if there are no commits, we're done.
1459
stop_revision = osutils.safe_revision_id(stop_revision)
1460
# whats the current last revision, before we fetch [and change it
1462
last_rev = self.last_revision()
1463
# we fetch here regardless of whether we need to so that we pickup
1465
self.fetch(other, stop_revision)
1466
my_ancestry = self.repository.get_ancestry(last_rev)
1467
if stop_revision in my_ancestry:
1468
# last_revision is a descendant of stop_revision
1470
self.generate_revision_history(stop_revision, last_rev=last_rev,
1475
def basis_tree(self):
1476
"""See Branch.basis_tree."""
1477
return self.repository.revision_tree(self.last_revision())
1479
@deprecated_method(zero_eight)
1480
def working_tree(self):
1481
"""Create a Working tree object for this branch."""
1483
from bzrlib.transport.local import LocalTransport
1484
if (self.base.find('://') != -1 or
1485
not isinstance(self._transport, LocalTransport)):
1486
raise NoWorkingTree(self.base)
1487
return self.bzrdir.open_workingtree()
1490
def pull(self, source, overwrite=False, stop_revision=None,
1491
_hook_master=None, _run_hooks=True):
1494
:param _hook_master: Private parameter - set the branch to
1495
be supplied as the master to push hooks.
1496
:param _run_hooks: Private parameter - allow disabling of
1497
hooks, used when pushing to a master branch.
1499
result = PullResult()
1500
result.source_branch = source
1501
result.target_branch = self
1504
result.old_revno, result.old_revid = self.last_revision_info()
1506
self.update_revisions(source, stop_revision)
1507
except DivergedBranches:
1511
if stop_revision is None:
1512
stop_revision = source.last_revision()
1513
self.generate_revision_history(stop_revision)
1514
result.tag_conflicts = source.tags.merge_to(self.tags)
1515
result.new_revno, result.new_revid = self.last_revision_info()
1517
result.master_branch = _hook_master
1518
result.local_branch = self
1520
result.master_branch = self
1521
result.local_branch = None
1523
for hook in Branch.hooks['post_pull']:
1529
def _get_parent_location(self):
1530
_locs = ['parent', 'pull', 'x-pull']
1533
return self.control_files.get(l).read().strip('\n')
1539
def push(self, target, overwrite=False, stop_revision=None,
1540
_hook_master=None, _run_hooks=True):
1543
:param _hook_master: Private parameter - set the branch to
1544
be supplied as the master to push hooks.
1545
:param _run_hooks: Private parameter - allow disabling of
1546
hooks, used when pushing to a master branch.
1548
result = PushResult()
1549
result.source_branch = self
1550
result.target_branch = target
1553
result.old_revno, result.old_revid = target.last_revision_info()
1555
target.update_revisions(self, stop_revision)
1556
except DivergedBranches:
1560
target.set_revision_history(self.revision_history())
1561
result.tag_conflicts = self.tags.merge_to(target.tags)
1562
result.new_revno, result.new_revid = target.last_revision_info()
1564
result.master_branch = _hook_master
1565
result.local_branch = target
1567
result.master_branch = target
1568
result.local_branch = None
1570
for hook in Branch.hooks['post_push']:
1576
def get_parent(self):
1577
"""See Branch.get_parent."""
1579
assert self.base[-1] == '/'
1580
parent = self._get_parent_location()
1583
# This is an old-format absolute path to a local branch
1584
# turn it into a url
1585
if parent.startswith('/'):
1586
parent = urlutils.local_path_to_url(parent.decode('utf8'))
1588
return urlutils.join(self.base[:-1], parent)
1589
except errors.InvalidURLJoin, e:
1590
raise errors.InaccessibleParent(parent, self.base)
1592
def get_push_location(self):
1593
"""See Branch.get_push_location."""
1594
push_loc = self.get_config().get_user_option('push_location')
1597
def set_push_location(self, location):
1598
"""See Branch.set_push_location."""
1599
self.get_config().set_user_option(
1600
'push_location', location,
1601
store=_mod_config.STORE_LOCATION_NORECURSE)
1604
def set_parent(self, url):
1605
"""See Branch.set_parent."""
1606
# TODO: Maybe delete old location files?
1607
# URLs should never be unicode, even on the local fs,
1608
# FIXUP this and get_parent in a future branch format bump:
1609
# read and rewrite the file, and have the new format code read
1610
# using .get not .get_utf8. RBC 20060125
1612
if isinstance(url, unicode):
1614
url = url.encode('ascii')
1615
except UnicodeEncodeError:
1616
raise bzrlib.errors.InvalidURL(url,
1617
"Urls must be 7-bit ascii, "
1618
"use bzrlib.urlutils.escape")
1619
url = urlutils.relative_url(self.base, url)
1620
self._set_parent_location(url)
1622
def _set_parent_location(self, url):
1624
self.control_files._transport.delete('parent')
1626
assert isinstance(url, str)
1627
self.control_files.put_bytes('parent', url + '\n')
1629
@deprecated_function(zero_nine)
1630
def tree_config(self):
1631
"""DEPRECATED; call get_config instead.
1632
TreeConfig has become part of BranchConfig."""
1633
return TreeConfig(self)
1636
class BzrBranch5(BzrBranch):
1637
"""A format 5 branch. This supports new features over plan branches.
1639
It has support for a master_branch which is the data for bound branches.
1647
super(BzrBranch5, self).__init__(_format=_format,
1648
_control_files=_control_files,
1650
_repository=_repository)
1653
def pull(self, source, overwrite=False, stop_revision=None,
1655
"""Extends branch.pull to be bound branch aware.
1657
:param _run_hooks: Private parameter used to force hook running
1658
off during bound branch double-pushing.
1660
bound_location = self.get_bound_location()
1661
master_branch = None
1662
if bound_location and source.base != bound_location:
1663
# not pulling from master, so we need to update master.
1664
master_branch = self.get_master_branch()
1665
master_branch.lock_write()
1668
# pull from source into master.
1669
master_branch.pull(source, overwrite, stop_revision,
1671
return super(BzrBranch5, self).pull(source, overwrite,
1672
stop_revision, _hook_master=master_branch,
1673
_run_hooks=_run_hooks)
1676
master_branch.unlock()
1679
def push(self, target, overwrite=False, stop_revision=None):
1680
"""Updates branch.push to be bound branch aware."""
1681
bound_location = target.get_bound_location()
1682
master_branch = None
1683
if bound_location and target.base != bound_location:
1684
# not pushing to master, so we need to update master.
1685
master_branch = target.get_master_branch()
1686
master_branch.lock_write()
1689
# push into the master from this branch.
1690
super(BzrBranch5, self).push(master_branch, overwrite,
1691
stop_revision, _run_hooks=False)
1692
# and push into the target branch from this. Note that we push from
1693
# this branch again, because its considered the highest bandwidth
1695
return super(BzrBranch5, self).push(target, overwrite,
1696
stop_revision, _hook_master=master_branch)
1699
master_branch.unlock()
1701
def get_bound_location(self):
1703
return self.control_files.get_utf8('bound').read()[:-1]
1704
except errors.NoSuchFile:
1708
def get_master_branch(self):
1709
"""Return the branch we are bound to.
1711
:return: Either a Branch, or None
1713
This could memoise the branch, but if thats done
1714
it must be revalidated on each new lock.
1715
So for now we just don't memoise it.
1716
# RBC 20060304 review this decision.
1718
bound_loc = self.get_bound_location()
1722
return Branch.open(bound_loc)
1723
except (errors.NotBranchError, errors.ConnectionError), e:
1724
raise errors.BoundBranchConnectionFailure(
1728
def set_bound_location(self, location):
1729
"""Set the target where this branch is bound to.
1731
:param location: URL to the target branch
1734
self.control_files.put_utf8('bound', location+'\n')
1737
self.control_files._transport.delete('bound')
1349
if filename == head:
1356
def gen_file_id(name):
1357
"""Return new file id.
1359
This should probably generate proper UUIDs, but for the moment we
1360
cope with just randomness because running uuidgen every time is
1363
from binascii import hexlify
1364
from time import time
1366
# get last component
1367
idx = name.rfind('/')
1369
name = name[idx+1 : ]
1370
idx = name.rfind('\\')
1372
name = name[idx+1 : ]
1374
# make it not a hidden file
1375
name = name.lstrip('.')
1377
# remove any wierd characters; we don't escape them but rather
1378
# just pull them out
1379
name = re.sub(r'[^\w.]', '', name)
1381
s = hexlify(rand_bytes(8))
1382
return '-'.join((name, compact_date(time()), s))
1386
"""Return a new tree-root file id."""
1387
return gen_file_id('TREE_ROOT')
1743
def bind(self, other):
1744
"""Bind this branch to the branch other.
1746
This does not push or pull data between the branches, though it does
1747
check for divergence to raise an error when the branches are not
1748
either the same, or one a prefix of the other. That behaviour may not
1749
be useful, so that check may be removed in future.
1751
:param other: The branch to bind to
1754
# TODO: jam 20051230 Consider checking if the target is bound
1755
# It is debatable whether you should be able to bind to
1756
# a branch which is itself bound.
1757
# Committing is obviously forbidden,
1758
# but binding itself may not be.
1759
# Since we *have* to check at commit time, we don't
1760
# *need* to check here
1762
# we want to raise diverged if:
1763
# last_rev is not in the other_last_rev history, AND
1764
# other_last_rev is not in our history, and do it without pulling
1766
last_rev = self.last_revision()
1767
if last_rev is not None:
1770
other_last_rev = other.last_revision()
1771
if other_last_rev is not None:
1772
# neither branch is new, we have to do some work to
1773
# ascertain diversion.
1774
remote_graph = other.repository.get_revision_graph(
1776
local_graph = self.repository.get_revision_graph(last_rev)
1777
if (last_rev not in remote_graph and
1778
other_last_rev not in local_graph):
1779
raise errors.DivergedBranches(self, other)
1782
self.set_bound_location(other.base)
1786
"""If bound, unbind"""
1787
return self.set_bound_location(None)
1791
"""Synchronise this branch with the master branch if any.
1793
:return: None or the last_revision that was pivoted out during the
1796
master = self.get_master_branch()
1797
if master is not None:
1798
old_tip = self.last_revision()
1799
self.pull(master, overwrite=True)
1800
if old_tip in self.repository.get_ancestry(self.last_revision()):
1806
class BzrBranchExperimental(BzrBranch5):
1807
"""Bzr experimental branch format
1810
- a revision-history file.
1812
- a lock dir guarding the branch itself
1813
- all of this stored in a branch/ subdirectory
1814
- works with shared repositories.
1815
- a tag dictionary in the branch
1817
This format is new in bzr 0.15, but shouldn't be used for real data,
1820
This class acts as it's own BranchFormat.
1823
_matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1826
def get_format_string(cls):
1827
"""See BranchFormat.get_format_string()."""
1828
return "Bazaar-NG branch format experimental\n"
1831
def get_format_description(cls):
1832
"""See BranchFormat.get_format_description()."""
1833
return "Experimental branch format"
1836
def _initialize_control_files(cls, a_bzrdir, utf8_files, lock_filename,
1838
branch_transport = a_bzrdir.get_branch_transport(cls)
1839
control_files = lockable_files.LockableFiles(branch_transport,
1840
lock_filename, lock_class)
1841
control_files.create_lock()
1842
control_files.lock_write()
1844
for filename, content in utf8_files:
1845
control_files.put_utf8(filename, content)
1847
control_files.unlock()
1850
def initialize(cls, a_bzrdir):
1851
"""Create a branch of this format in a_bzrdir."""
1852
utf8_files = [('format', cls.get_format_string()),
1853
('revision-history', ''),
1854
('branch-name', ''),
1857
cls._initialize_control_files(a_bzrdir, utf8_files,
1858
'lock', lockdir.LockDir)
1859
return cls.open(a_bzrdir, _found=True)
1862
def open(cls, a_bzrdir, _found=False):
1863
"""Return the branch object for a_bzrdir
1865
_found is a private parameter, do not use it. It is used to indicate
1866
if format probing has already be done.
1869
format = BranchFormat.find_format(a_bzrdir)
1870
assert format.__class__ == cls
1871
transport = a_bzrdir.get_branch_transport(None)
1872
control_files = lockable_files.LockableFiles(transport, 'lock',
1874
return cls(_format=cls,
1875
_control_files=control_files,
1877
_repository=a_bzrdir.find_repository())
1880
def is_supported(cls):
1883
def _make_tags(self):
1884
return BasicTags(self)
1887
def supports_tags(cls):
1891
BranchFormat.register_format(BzrBranchExperimental)
1894
class BzrBranch6(BzrBranch5):
1897
def last_revision_info(self):
1898
revision_string = self.control_files.get('last-revision').read()
1899
revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
1900
revision_id = cache_utf8.get_cached_utf8(revision_id)
1902
return revno, revision_id
1904
def last_revision(self):
1905
"""Return last revision id, or None"""
1906
revision_id = self.last_revision_info()[1]
1907
if revision_id == _mod_revision.NULL_REVISION:
1911
def _write_last_revision_info(self, revno, revision_id):
1912
"""Simply write out the revision id, with no checks.
1914
Use set_last_revision_info to perform this safely.
1916
Does not update the revision_history cache.
1917
Intended to be called by set_last_revision_info and
1918
_write_revision_history.
1920
if revision_id is None:
1921
revision_id = 'null:'
1922
out_string = '%d %s\n' % (revno, revision_id)
1923
self.control_files.put_bytes('last-revision', out_string)
1926
def set_last_revision_info(self, revno, revision_id):
1927
revision_id = osutils.safe_revision_id(revision_id)
1928
if self._get_append_revisions_only():
1929
self._check_history_violation(revision_id)
1930
self._write_last_revision_info(revno, revision_id)
1931
transaction = self.get_transaction()
1932
cached_history = transaction.map.find_revision_history()
1933
if cached_history is not None:
1934
transaction.map.remove_object(cached_history)
1936
def _check_history_violation(self, revision_id):
1937
last_revision = self.last_revision()
1938
if last_revision is None:
1940
if last_revision not in self._lefthand_history(revision_id):
1941
raise errors.AppendRevisionsOnlyViolation(self.base)
1943
def _gen_revision_history(self):
1944
"""Generate the revision history from last revision
1946
history = list(self.repository.iter_reverse_revision_history(
1947
self.last_revision()))
1951
def _write_revision_history(self, history):
1952
"""Factored out of set_revision_history.
1954
This performs the actual writing to disk, with format-specific checks.
1955
It is intended to be called by BzrBranch5.set_revision_history.
1957
if len(history) == 0:
1958
last_revision = 'null:'
1960
if history != self._lefthand_history(history[-1]):
1961
raise errors.NotLefthandHistory(history)
1962
last_revision = history[-1]
1963
if self._get_append_revisions_only():
1964
self._check_history_violation(last_revision)
1965
self._write_last_revision_info(len(history), last_revision)
1968
def append_revision(self, *revision_ids):
1969
revision_ids = [osutils.safe_revision_id(r) for r in revision_ids]
1970
if len(revision_ids) == 0:
1972
prev_revno, prev_revision = self.last_revision_info()
1973
for revision in self.repository.get_revisions(revision_ids):
1974
if prev_revision == _mod_revision.NULL_REVISION:
1975
if revision.parent_ids != []:
1976
raise errors.NotLeftParentDescendant(self, prev_revision,
1977
revision.revision_id)
1979
if revision.parent_ids[0] != prev_revision:
1980
raise errors.NotLeftParentDescendant(self, prev_revision,
1981
revision.revision_id)
1982
prev_revision = revision.revision_id
1983
self.set_last_revision_info(prev_revno + len(revision_ids),
1987
def _set_parent_location(self, url):
1988
"""Set the parent branch"""
1989
self._set_config_location('parent_location', url, make_relative=True)
1992
def _get_parent_location(self):
1993
"""Set the parent branch"""
1994
return self._get_config_location('parent_location')
1996
def set_push_location(self, location):
1997
"""See Branch.set_push_location."""
1998
self._set_config_location('push_location', location)
2000
def set_bound_location(self, location):
2001
"""See Branch.set_push_location."""
2003
config = self.get_config()
2004
if location is None:
2005
if config.get_user_option('bound') != 'True':
2008
config.set_user_option('bound', 'False')
2011
self._set_config_location('bound_location', location,
2013
config.set_user_option('bound', 'True')
2016
def _get_bound_location(self, bound):
2017
"""Return the bound location in the config file.
2019
Return None if the bound parameter does not match"""
2020
config = self.get_config()
2021
config_bound = (config.get_user_option('bound') == 'True')
2022
if config_bound != bound:
2024
return self._get_config_location('bound_location', config=config)
2026
def get_bound_location(self):
2027
"""See Branch.set_push_location."""
2028
return self._get_bound_location(True)
2030
def get_old_bound_location(self):
2031
"""See Branch.get_old_bound_location"""
2032
return self._get_bound_location(False)
2034
def set_append_revisions_only(self, enabled):
2039
self.get_config().set_user_option('append_revisions_only', value)
2041
def _get_append_revisions_only(self):
2042
value = self.get_config().get_user_option('append_revisions_only')
2043
return value == 'True'
2045
def _synchronize_history(self, destination, revision_id):
2046
"""Synchronize last revision and revision history between branches.
2048
This version is most efficient when the destination is also a
2049
BzrBranch6, but works for BzrBranch5, as long as the destination's
2050
repository contains all the lefthand ancestors of the intended
2051
last_revision. If not, set_last_revision_info will fail.
2053
:param destination: The branch to copy the history into
2054
:param revision_id: The revision-id to truncate history at. May
2055
be None to copy complete history.
2057
if revision_id is None:
2058
revno, revision_id = self.last_revision_info()
2060
revno = self.revision_id_to_revno(revision_id)
2061
destination.set_last_revision_info(revno, revision_id)
2063
def _make_tags(self):
2064
return BasicTags(self)
2067
class BranchTestProviderAdapter(object):
2068
"""A tool to generate a suite testing multiple branch formats at once.
2070
This is done by copying the test once for each transport and injecting
2071
the transport_server, transport_readonly_server, and branch_format
2072
classes into each copy. Each copy is also given a new id() to make it
2076
def __init__(self, transport_server, transport_readonly_server, formats):
2077
self._transport_server = transport_server
2078
self._transport_readonly_server = transport_readonly_server
2079
self._formats = formats
2081
def adapt(self, test):
2082
result = TestSuite()
2083
for branch_format, bzrdir_format in self._formats:
2084
new_test = deepcopy(test)
2085
new_test.transport_server = self._transport_server
2086
new_test.transport_readonly_server = self._transport_readonly_server
2087
new_test.bzrdir_format = bzrdir_format
2088
new_test.branch_format = branch_format
2089
def make_new_test_id():
2090
# the format can be either a class or an instance
2091
name = getattr(branch_format, '__name__',
2092
branch_format.__class__.__name__)
2093
new_id = "%s(%s)" % (new_test.id(), name)
2094
return lambda: new_id
2095
new_test.id = make_new_test_id()
2096
result.addTest(new_test)
2100
######################################################################
2101
# results of operations
2104
class _Result(object):
2106
def _show_tag_conficts(self, to_file):
2107
if not getattr(self, 'tag_conflicts', None):
2109
to_file.write('Conflicting tags:\n')
2110
for name, value1, value2 in self.tag_conflicts:
2111
to_file.write(' %s\n' % (name, ))
2114
class PullResult(_Result):
2115
"""Result of a Branch.pull operation.
2117
:ivar old_revno: Revision number before pull.
2118
:ivar new_revno: Revision number after pull.
2119
:ivar old_revid: Tip revision id before pull.
2120
:ivar new_revid: Tip revision id after pull.
2121
:ivar source_branch: Source (local) branch object.
2122
:ivar master_branch: Master branch of the target, or None.
2123
:ivar target_branch: Target/destination branch object.
2127
# DEPRECATED: pull used to return the change in revno
2128
return self.new_revno - self.old_revno
2130
def report(self, to_file):
2131
if self.old_revid == self.new_revid:
2132
to_file.write('No revisions to pull.\n')
2134
to_file.write('Now on revision %d.\n' % self.new_revno)
2135
self._show_tag_conficts(to_file)
2138
class PushResult(_Result):
2139
"""Result of a Branch.push operation.
2141
:ivar old_revno: Revision number before push.
2142
:ivar new_revno: Revision number after push.
2143
:ivar old_revid: Tip revision id before push.
2144
:ivar new_revid: Tip revision id after push.
2145
:ivar source_branch: Source branch object.
2146
:ivar master_branch: Master branch of the target, or None.
2147
:ivar target_branch: Target/destination branch object.
2151
# DEPRECATED: push used to return the change in revno
2152
return self.new_revno - self.old_revno
2154
def report(self, to_file):
2155
"""Write a human-readable description of the result."""
2156
if self.old_revid == self.new_revid:
2157
to_file.write('No new revisions to push.\n')
2159
to_file.write('Pushed up to revision %d.\n' % self.new_revno)
2160
self._show_tag_conficts(to_file)
2163
class BranchCheckResult(object):
2164
"""Results of checking branch consistency.
2169
def __init__(self, branch):
2170
self.branch = branch
2172
def report_results(self, verbose):
2173
"""Report the check results via trace.note.
2175
:param verbose: Requests more detailed display of what was checked,
2178
note('checked branch %s format %s',
2180
self.branch._format)
2183
class Converter5to6(object):
2184
"""Perform an in-place upgrade of format 5 to format 6"""
2186
def convert(self, branch):
2187
# Data for 5 and 6 can peacefully coexist.
2188
format = BzrBranchFormat6()
2189
new_branch = format.open(branch.bzrdir, _found=True)
2191
# Copy source data into target
2192
new_branch.set_last_revision_info(*branch.last_revision_info())
2193
new_branch.set_parent(branch.get_parent())
2194
new_branch.set_bound_location(branch.get_bound_location())
2195
new_branch.set_push_location(branch.get_push_location())
2197
# New branch has no tags by default
2198
new_branch.tags._set_tag_dict({})
2200
# Copying done; now update target format
2201
new_branch.control_files.put_utf8('format',
2202
format.get_format_string())
2204
# Clean up old files
2205
new_branch.control_files._transport.delete('revision-history')
2207
branch.set_parent(None)
2210
branch.set_bound_location(None)