153
154
self.cache_root = cache_root
156
class _Branch(Branch):
157
cfg = self.tree_config()
158
return cfg.get_option(u"nickname", default=self.base.split('/')[-1])
160
def _set_nick(self, nick):
161
cfg = self.tree_config()
162
cfg.set_option(nick, "nickname")
163
assert cfg.get_option("nickname") == nick
165
nick = property(_get_nick, _set_nick)
167
def push_stores(self, branch_to):
168
"""Copy the content of this branches store to branch_to."""
169
raise NotImplementedError('push_stores is abstract')
171
def get_transaction(self):
172
"""Return the current active transaction.
174
If no transaction is active, this returns a passthrough object
175
for which all data is immediately flushed and no caching happens.
177
raise NotImplementedError('get_transaction is abstract')
179
def lock_write(self):
180
raise NotImplementedError('lock_write is abstract')
183
raise NotImplementedError('lock_read is abstract')
186
raise NotImplementedError('unlock is abstract')
188
def abspath(self, name):
189
"""Return absolute filename for something in the branch
191
XXX: Robert Collins 20051017 what is this used for? why is it a branch
192
method and not a tree method.
194
raise NotImplementedError('abspath is abstract')
196
def controlfilename(self, file_or_path):
197
"""Return location relative to branch."""
198
raise NotImplementedError('controlfilename is abstract')
200
def controlfile(self, file_or_path, mode='r'):
201
"""Open a control file for this branch.
203
There are two classes of file in the control directory: text
204
and binary. binary files are untranslated byte streams. Text
205
control files are stored with Unix newlines and in UTF-8, even
206
if the platform or locale defaults are different.
208
Controlfiles should almost never be opened in write mode but
209
rather should be atomically copied and replaced using atomicfile.
211
raise NotImplementedError('controlfile is abstract')
213
def put_controlfile(self, path, f, encode=True):
214
"""Write an entry as a controlfile.
216
:param path: The path to put the file, relative to the .bzr control
218
:param f: A file-like or string object whose contents should be copied.
219
:param encode: If true, encode the contents as utf-8
221
raise NotImplementedError('put_controlfile is abstract')
223
def put_controlfiles(self, files, encode=True):
224
"""Write several entries as controlfiles.
226
:param files: A list of [(path, file)] pairs, where the path is the directory
227
underneath the bzr control directory
228
:param encode: If true, encode the contents as utf-8
230
raise NotImplementedError('put_controlfiles is abstract')
232
def get_root_id(self):
233
"""Return the id of this branches root"""
234
raise NotImplementedError('get_root_id is abstract')
236
def print_file(self, file, revno):
237
"""Print `file` to stdout."""
238
raise NotImplementedError('print_file is abstract')
240
def append_revision(self, *revision_ids):
241
raise NotImplementedError('append_revision is abstract')
243
def set_revision_history(self, rev_history):
244
raise NotImplementedError('set_revision_history is abstract')
246
def has_revision(self, revision_id):
247
"""True if this branch has a copy of the revision.
249
This does not necessarily imply the revision is merge
250
or on the mainline."""
251
raise NotImplementedError('has_revision is abstract')
253
def get_revision_xml_file(self, revision_id):
254
"""Return XML file object for revision object."""
255
raise NotImplementedError('get_revision_xml_file is abstract')
257
def get_revision_xml(self, revision_id):
258
raise NotImplementedError('get_revision_xml is abstract')
260
def get_revision(self, revision_id):
261
"""Return the Revision object for a named revision"""
262
raise NotImplementedError('get_revision is abstract')
264
def get_revision_delta(self, revno):
265
"""Return the delta for one revision.
267
The delta is relative to its mainline predecessor, or the
268
empty tree for revision 1.
270
assert isinstance(revno, int)
271
rh = self.revision_history()
272
if not (1 <= revno <= len(rh)):
273
raise InvalidRevisionNumber(revno)
275
# revno is 1-based; list is 0-based
277
new_tree = self.revision_tree(rh[revno-1])
279
old_tree = EmptyTree()
281
old_tree = self.revision_tree(rh[revno-2])
283
return compare_trees(old_tree, new_tree)
285
def get_revision_sha1(self, revision_id):
286
"""Hash the stored value of a revision, and return it."""
287
raise NotImplementedError('get_revision_sha1 is abstract')
289
def get_ancestry(self, revision_id):
290
"""Return a list of revision-ids integrated by a revision.
292
This currently returns a list, but the ordering is not guaranteed:
295
raise NotImplementedError('get_ancestry is abstract')
297
def get_inventory(self, revision_id):
298
"""Get Inventory object by hash."""
299
raise NotImplementedError('get_inventory is abstract')
301
def get_inventory_xml(self, revision_id):
302
"""Get inventory XML as a file object."""
303
raise NotImplementedError('get_inventory_xml is abstract')
305
def get_inventory_sha1(self, revision_id):
306
"""Return the sha1 hash of the inventory entry."""
307
raise NotImplementedError('get_inventory_sha1 is abstract')
309
def get_revision_inventory(self, revision_id):
310
"""Return inventory of a past revision."""
311
raise NotImplementedError('get_revision_inventory is abstract')
313
def revision_history(self):
314
"""Return sequence of revision hashes on to this branch."""
315
raise NotImplementedError('revision_history is abstract')
318
"""Return current revision number for this branch.
320
That is equivalent to the number of revisions committed to
323
return len(self.revision_history())
325
def last_revision(self):
326
"""Return last patch hash, or None if no history."""
327
ph = self.revision_history()
333
def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
334
"""Return a list of new revisions that would perfectly fit.
336
If self and other have not diverged, return a list of the revisions
337
present in other, but missing from self.
339
>>> from bzrlib.commit import commit
340
>>> bzrlib.trace.silent = True
341
>>> br1 = ScratchBranch()
342
>>> br2 = ScratchBranch()
343
>>> br1.missing_revisions(br2)
345
>>> commit(br2, "lala!", rev_id="REVISION-ID-1")
346
>>> br1.missing_revisions(br2)
348
>>> br2.missing_revisions(br1)
350
>>> commit(br1, "lala!", rev_id="REVISION-ID-1")
351
>>> br1.missing_revisions(br2)
353
>>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
354
>>> br1.missing_revisions(br2)
356
>>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
357
>>> br1.missing_revisions(br2)
358
Traceback (most recent call last):
359
DivergedBranches: These branches have diverged.
361
self_history = self.revision_history()
362
self_len = len(self_history)
363
other_history = other.revision_history()
364
other_len = len(other_history)
365
common_index = min(self_len, other_len) -1
366
if common_index >= 0 and \
367
self_history[common_index] != other_history[common_index]:
368
raise DivergedBranches(self, other)
370
if stop_revision is None:
371
stop_revision = other_len
373
assert isinstance(stop_revision, int)
374
if stop_revision > other_len:
375
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
376
return other_history[self_len:stop_revision]
378
def update_revisions(self, other, stop_revision=None):
379
"""Pull in new perfect-fit revisions."""
380
raise NotImplementedError('update_revisions is abstract')
382
def pullable_revisions(self, other, stop_revision):
383
raise NotImplementedError('pullable_revisions is abstract')
385
def revision_id_to_revno(self, revision_id):
386
"""Given a revision id, return its revno"""
387
if revision_id is None:
389
history = self.revision_history()
391
return history.index(revision_id) + 1
393
raise bzrlib.errors.NoSuchRevision(self, revision_id)
395
def get_rev_id(self, revno, history=None):
396
"""Find the revision id of the specified revno."""
400
history = self.revision_history()
401
elif revno <= 0 or revno > len(history):
402
raise bzrlib.errors.NoSuchRevision(self, revno)
403
return history[revno - 1]
405
def revision_tree(self, revision_id):
406
"""Return Tree for a revision on this branch.
408
`revision_id` may be None for the null revision, in which case
409
an `EmptyTree` is returned."""
410
raise NotImplementedError('revision_tree is abstract')
412
def working_tree(self):
413
"""Return a `Tree` for the working copy if this is a local branch."""
414
raise NotImplementedError('working_tree is abstract')
416
def pull(self, source, overwrite=False):
417
raise NotImplementedError('pull is abstract')
419
def basis_tree(self):
420
"""Return `Tree` object for last revision.
422
If there are no revisions yet, return an `EmptyTree`.
424
return self.revision_tree(self.last_revision())
426
def rename_one(self, from_rel, to_rel):
429
This can change the directory or the filename or both.
431
raise NotImplementedError('rename_one is abstract')
433
def move(self, from_paths, to_name):
436
to_name must exist as a versioned directory.
438
If to_name exists and is a directory, the files are moved into
439
it, keeping their old names. If it is a directory,
441
Note that to_name is only the last component of the new name;
442
this doesn't change the directory.
444
This returns a list of (from_path, to_path) pairs for each
447
raise NotImplementedError('move is abstract')
449
def revert(self, filenames, old_tree=None, backups=True):
450
"""Restore selected files to the versions from a previous tree.
453
If true (default) backups are made of files before
456
raise NotImplementedError('revert is abstract')
458
def pending_merges(self):
459
"""Return a list of pending merges.
461
These are revisions that have been merged into the working
462
directory but not yet committed.
464
raise NotImplementedError('pending_merges is abstract')
466
def add_pending_merge(self, *revision_ids):
467
# TODO: Perhaps should check at this point that the
468
# history of the revision is actually present?
469
raise NotImplementedError('add_pending_merge is abstract')
471
def set_pending_merges(self, rev_list):
472
raise NotImplementedError('set_pending_merges is abstract')
474
def get_parent(self):
475
"""Return the parent location of the branch.
477
This is the default location for push/pull/missing. The usual
478
pattern is that the user can override it by specifying a
481
raise NotImplementedError('get_parent is abstract')
483
def get_push_location(self):
484
"""Return the None or the location to push this branch to."""
485
raise NotImplementedError('get_push_location is abstract')
487
def set_push_location(self, location):
488
"""Set a new push location for this branch."""
489
raise NotImplementedError('set_push_location is abstract')
491
def set_parent(self, url):
492
raise NotImplementedError('set_parent is abstract')
494
def check_revno(self, revno):
496
Check whether a revno corresponds to any revision.
497
Zero (the NULL revision) is considered valid.
500
self.check_real_revno(revno)
502
def check_real_revno(self, revno):
504
Check whether a revno corresponds to a real revision.
505
Zero (the NULL revision) is considered invalid
507
if revno < 1 or revno > self.revno():
508
raise InvalidRevisionNumber(revno)
510
def sign_revision(self, revision_id, gpg_strategy):
511
raise NotImplementedError('sign_revision is abstract')
513
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
514
raise NotImplementedError('store_revision_signature is abstract')
516
class BzrBranch(Branch):
157
517
"""A branch stored in the actual filesystem.
159
519
Note that it's "local" in the context of the filesystem; it doesn't
674
981
# transaction.register_clean(history, precious=True)
675
982
return list(history)
678
"""Return current revision number for this branch.
680
That is equivalent to the number of revisions committed to
683
return len(self.revision_history())
685
def last_revision(self):
686
"""Return last patch hash, or None if no history.
688
ph = self.revision_history()
694
def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
695
"""Return a list of new revisions that would perfectly fit.
697
If self and other have not diverged, return a list of the revisions
698
present in other, but missing from self.
700
>>> from bzrlib.commit import commit
701
>>> bzrlib.trace.silent = True
702
>>> br1 = ScratchBranch()
703
>>> br2 = ScratchBranch()
704
>>> br1.missing_revisions(br2)
706
>>> commit(br2, "lala!", rev_id="REVISION-ID-1")
707
>>> br1.missing_revisions(br2)
709
>>> br2.missing_revisions(br1)
711
>>> commit(br1, "lala!", rev_id="REVISION-ID-1")
712
>>> br1.missing_revisions(br2)
714
>>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
715
>>> br1.missing_revisions(br2)
717
>>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
718
>>> br1.missing_revisions(br2)
719
Traceback (most recent call last):
720
DivergedBranches: These branches have diverged.
722
self_history = self.revision_history()
723
self_len = len(self_history)
724
other_history = other.revision_history()
725
other_len = len(other_history)
726
common_index = min(self_len, other_len) -1
727
if common_index >= 0 and \
728
self_history[common_index] != other_history[common_index]:
729
raise DivergedBranches(self, other)
731
if stop_revision is None:
732
stop_revision = other_len
734
assert isinstance(stop_revision, int)
735
if stop_revision > other_len:
736
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
737
return other_history[self_len:stop_revision]
739
984
def update_revisions(self, other, stop_revision=None):
740
"""Pull in new perfect-fit revisions."""
985
"""See Branch.update_revisions."""
741
986
from bzrlib.fetch import greedy_fetch
742
987
if stop_revision is None:
743
988
stop_revision = other.last_revision()