155
155
self.cache_root = cache_root
158
class _Branch(Branch):
157
def push_stores(self, branch_to):
158
"""Copy the content of this branches store to branch_to."""
159
raise NotImplementedError('push_stores is abstract')
161
def get_transaction(self):
162
"""Return the current active transaction.
164
If no transaction is active, this returns a passthrough object
165
for which all data is immediately flushed and no caching happens.
167
raise NotImplementedError('get_transaction is abstract')
169
def lock_write(self):
170
raise NotImplementedError('lock_write is abstract')
173
raise NotImplementedError('lock_read is abstract')
176
raise NotImplementedError('unlock is abstract')
178
def abspath(self, name):
179
"""Return absolute filename for something in the branch
181
XXX: Robert Collins 20051017 what is this used for? why is it a branch
182
method and not a tree method.
184
raise NotImplementedError('abspath is abstract')
186
def controlfilename(self, file_or_path):
187
"""Return location relative to branch."""
188
raise NotImplementedError('controlfilename is abstract')
190
def controlfile(self, file_or_path, mode='r'):
191
"""Open a control file for this branch.
193
There are two classes of file in the control directory: text
194
and binary. binary files are untranslated byte streams. Text
195
control files are stored with Unix newlines and in UTF-8, even
196
if the platform or locale defaults are different.
198
Controlfiles should almost never be opened in write mode but
199
rather should be atomically copied and replaced using atomicfile.
201
raise NotImplementedError('controlfile is abstract')
203
def put_controlfile(self, path, f, encode=True):
204
"""Write an entry as a controlfile.
206
:param path: The path to put the file, relative to the .bzr control
208
:param f: A file-like or string object whose contents should be copied.
209
:param encode: If true, encode the contents as utf-8
211
raise NotImplementedError('put_controlfile is abstract')
213
def put_controlfiles(self, files, encode=True):
214
"""Write several entries as controlfiles.
216
:param files: A list of [(path, file)] pairs, where the path is the directory
217
underneath the bzr control directory
218
:param encode: If true, encode the contents as utf-8
220
raise NotImplementedError('put_controlfiles is abstract')
222
def get_root_id(self):
223
"""Return the id of this branches root"""
224
raise NotImplementedError('get_root_id is abstract')
226
def set_root_id(self, file_id):
227
raise NotImplementedError('set_root_id is abstract')
229
def add(self, files, ids=None):
230
"""Make files versioned.
232
Note that the command line normally calls smart_add instead,
233
which can automatically recurse.
235
This puts the files in the Added state, so that they will be
236
recorded by the next commit.
239
List of paths to add, relative to the base of the tree.
242
If set, use these instead of automatically generated ids.
243
Must be the same length as the list of files, but may
244
contain None for ids that are to be autogenerated.
246
TODO: Perhaps have an option to add the ids even if the files do
249
TODO: Perhaps yield the ids and paths as they're added.
251
raise NotImplementedError('add is abstract')
253
def print_file(self, file, revno):
254
"""Print `file` to stdout."""
255
raise NotImplementedError('print_file is abstract')
258
"""Return all unknown files.
260
These are files in the working directory that are not versioned or
261
control files or ignored.
263
>>> from bzrlib.workingtree import WorkingTree
264
>>> b = ScratchBranch(files=['foo', 'foo~'])
265
>>> map(str, b.unknowns())
268
>>> list(b.unknowns())
270
>>> WorkingTree(b.base, b).remove('foo')
271
>>> list(b.unknowns())
274
raise NotImplementedError('unknowns is abstract')
276
def append_revision(self, *revision_ids):
277
raise NotImplementedError('append_revision is abstract')
279
def set_revision_history(self, rev_history):
280
raise NotImplementedError('set_revision_history is abstract')
282
def has_revision(self, revision_id):
283
"""True if this branch has a copy of the revision.
285
This does not necessarily imply the revision is merge
286
or on the mainline."""
287
raise NotImplementedError('has_revision is abstract')
289
def get_revision_xml_file(self, revision_id):
290
"""Return XML file object for revision object."""
291
raise NotImplementedError('get_revision_xml_file is abstract')
293
def get_revision_xml(self, revision_id):
294
raise NotImplementedError('get_revision_xml is abstract')
296
def get_revision(self, revision_id):
297
"""Return the Revision object for a named revision"""
298
raise NotImplementedError('get_revision is abstract')
300
def get_revision_delta(self, revno):
301
"""Return the delta for one revision.
303
The delta is relative to its mainline predecessor, or the
304
empty tree for revision 1.
306
assert isinstance(revno, int)
307
rh = self.revision_history()
308
if not (1 <= revno <= len(rh)):
309
raise InvalidRevisionNumber(revno)
311
# revno is 1-based; list is 0-based
313
new_tree = self.revision_tree(rh[revno-1])
315
old_tree = EmptyTree()
317
old_tree = self.revision_tree(rh[revno-2])
319
return compare_trees(old_tree, new_tree)
321
def get_revision_sha1(self, revision_id):
322
"""Hash the stored value of a revision, and return it."""
323
raise NotImplementedError('get_revision_sha1 is abstract')
325
def get_ancestry(self, revision_id):
326
"""Return a list of revision-ids integrated by a revision.
328
This currently returns a list, but the ordering is not guaranteed:
331
raise NotImplementedError('get_ancestry is abstract')
333
def get_inventory(self, revision_id):
334
"""Get Inventory object by hash."""
335
raise NotImplementedError('get_inventory is abstract')
337
def get_inventory_xml(self, revision_id):
338
"""Get inventory XML as a file object."""
339
raise NotImplementedError('get_inventory_xml is abstract')
341
def get_inventory_sha1(self, revision_id):
342
"""Return the sha1 hash of the inventory entry."""
343
raise NotImplementedError('get_inventory_sha1 is abstract')
345
def get_revision_inventory(self, revision_id):
346
"""Return inventory of a past revision."""
347
raise NotImplementedError('get_revision_inventory is abstract')
349
def revision_history(self):
350
"""Return sequence of revision hashes on to this branch."""
351
raise NotImplementedError('revision_history is abstract')
354
"""Return current revision number for this branch.
356
That is equivalent to the number of revisions committed to
359
return len(self.revision_history())
361
def last_revision(self):
362
"""Return last patch hash, or None if no history."""
363
ph = self.revision_history()
369
def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
370
"""Return a list of new revisions that would perfectly fit.
372
If self and other have not diverged, return a list of the revisions
373
present in other, but missing from self.
375
>>> from bzrlib.commit import commit
376
>>> bzrlib.trace.silent = True
377
>>> br1 = ScratchBranch()
378
>>> br2 = ScratchBranch()
379
>>> br1.missing_revisions(br2)
381
>>> commit(br2, "lala!", rev_id="REVISION-ID-1")
382
>>> br1.missing_revisions(br2)
384
>>> br2.missing_revisions(br1)
386
>>> commit(br1, "lala!", rev_id="REVISION-ID-1")
387
>>> br1.missing_revisions(br2)
389
>>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
390
>>> br1.missing_revisions(br2)
392
>>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
393
>>> br1.missing_revisions(br2)
394
Traceback (most recent call last):
395
DivergedBranches: These branches have diverged.
397
self_history = self.revision_history()
398
self_len = len(self_history)
399
other_history = other.revision_history()
400
other_len = len(other_history)
401
common_index = min(self_len, other_len) -1
402
if common_index >= 0 and \
403
self_history[common_index] != other_history[common_index]:
404
raise DivergedBranches(self, other)
406
if stop_revision is None:
407
stop_revision = other_len
409
assert isinstance(stop_revision, int)
410
if stop_revision > other_len:
411
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
412
return other_history[self_len:stop_revision]
414
def update_revisions(self, other, stop_revision=None):
415
"""Pull in new perfect-fit revisions."""
416
raise NotImplementedError('update_revisions is abstract')
418
def pullable_revisions(self, other, stop_revision):
419
raise NotImplementedError('pullable_revisions is abstract')
421
def commit(self, *args, **kw):
422
raise NotImplementedError('commit is abstract')
424
def revision_id_to_revno(self, revision_id):
425
"""Given a revision id, return its revno"""
426
if revision_id is None:
428
history = self.revision_history()
430
return history.index(revision_id) + 1
432
raise bzrlib.errors.NoSuchRevision(self, revision_id)
434
def get_rev_id(self, revno, history=None):
435
"""Find the revision id of the specified revno."""
439
history = self.revision_history()
440
elif revno <= 0 or revno > len(history):
441
raise bzrlib.errors.NoSuchRevision(self, revno)
442
return history[revno - 1]
444
def revision_tree(self, revision_id):
445
"""Return Tree for a revision on this branch.
447
`revision_id` may be None for the null revision, in which case
448
an `EmptyTree` is returned."""
449
raise NotImplementedError('revision_tree is abstract')
451
def working_tree(self):
452
"""Return a `Tree` for the working copy."""
453
raise NotImplementedError('working_tree is abstract')
455
def pull(self, source, overwrite=False):
456
raise NotImplementedError('pull is abstract')
458
def basis_tree(self):
459
"""Return `Tree` object for last revision.
461
If there are no revisions yet, return an `EmptyTree`.
463
return self.revision_tree(self.last_revision())
465
def rename_one(self, from_rel, to_rel):
468
This can change the directory or the filename or both.
470
raise NotImplementedError('rename_one is abstract')
472
def move(self, from_paths, to_name):
475
to_name must exist as a versioned directory.
477
If to_name exists and is a directory, the files are moved into
478
it, keeping their old names. If it is a directory,
480
Note that to_name is only the last component of the new name;
481
this doesn't change the directory.
483
This returns a list of (from_path, to_path) pairs for each
486
raise NotImplementedError('move is abstract')
488
def revert(self, filenames, old_tree=None, backups=True):
489
"""Restore selected files to the versions from a previous tree.
492
If true (default) backups are made of files before
495
raise NotImplementedError('revert is abstract')
497
def pending_merges(self):
498
"""Return a list of pending merges.
500
These are revisions that have been merged into the working
501
directory but not yet committed.
503
raise NotImplementedError('pending_merges is abstract')
505
def add_pending_merge(self, *revision_ids):
506
# TODO: Perhaps should check at this point that the
507
# history of the revision is actually present?
508
raise NotImplementedError('add_pending_merge is abstract')
510
def set_pending_merges(self, rev_list):
511
raise NotImplementedError('set_pending_merges is abstract')
513
def get_parent(self):
514
"""Return the parent location of the branch.
516
This is the default location for push/pull/missing. The usual
517
pattern is that the user can override it by specifying a
520
raise NotImplementedError('get_parent is abstract')
522
def get_push_location(self):
523
"""Return the None or the location to push this branch to."""
524
raise NotImplementedError('get_push_location is abstract')
526
def set_push_location(self, location):
527
"""Set a new push location for this branch."""
528
raise NotImplementedError('set_push_location is abstract')
530
def set_parent(self, url):
531
raise NotImplementedError('set_parent is abstract')
533
def check_revno(self, revno):
535
Check whether a revno corresponds to any revision.
536
Zero (the NULL revision) is considered valid.
539
self.check_real_revno(revno)
541
def check_real_revno(self, revno):
543
Check whether a revno corresponds to a real revision.
544
Zero (the NULL revision) is considered invalid
546
if revno < 1 or revno > self.revno():
547
raise InvalidRevisionNumber(revno)
549
def sign_revision(self, revision_id, gpg_strategy):
550
raise NotImplementedError('sign_revision is abstract')
552
def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
553
raise NotImplementedError('store_revision_signature is abstract')
555
class BzrBranch(Branch):
159
556
"""A branch stored in the actual filesystem.
161
558
Note that it's "local" in the context of the filesystem; it doesn't
793
1102
# transaction.register_clean(history, precious=True)
794
1103
return list(history)
797
"""Return current revision number for this branch.
799
That is equivalent to the number of revisions committed to
802
return len(self.revision_history())
804
def last_revision(self):
805
"""Return last patch hash, or None if no history.
807
ph = self.revision_history()
813
def missing_revisions(self, other, stop_revision=None, diverged_ok=False):
814
"""Return a list of new revisions that would perfectly fit.
816
If self and other have not diverged, return a list of the revisions
817
present in other, but missing from self.
819
>>> from bzrlib.commit import commit
820
>>> bzrlib.trace.silent = True
821
>>> br1 = ScratchBranch()
822
>>> br2 = ScratchBranch()
823
>>> br1.missing_revisions(br2)
825
>>> commit(br2, "lala!", rev_id="REVISION-ID-1")
826
>>> br1.missing_revisions(br2)
828
>>> br2.missing_revisions(br1)
830
>>> commit(br1, "lala!", rev_id="REVISION-ID-1")
831
>>> br1.missing_revisions(br2)
833
>>> commit(br2, "lala!", rev_id="REVISION-ID-2A")
834
>>> br1.missing_revisions(br2)
836
>>> commit(br1, "lala!", rev_id="REVISION-ID-2B")
837
>>> br1.missing_revisions(br2)
838
Traceback (most recent call last):
839
DivergedBranches: These branches have diverged.
841
self_history = self.revision_history()
842
self_len = len(self_history)
843
other_history = other.revision_history()
844
other_len = len(other_history)
845
common_index = min(self_len, other_len) -1
846
if common_index >= 0 and \
847
self_history[common_index] != other_history[common_index]:
848
raise DivergedBranches(self, other)
850
if stop_revision is None:
851
stop_revision = other_len
853
assert isinstance(stop_revision, int)
854
if stop_revision > other_len:
855
raise bzrlib.errors.NoSuchRevision(self, stop_revision)
856
return other_history[self_len:stop_revision]
858
1105
def update_revisions(self, other, stop_revision=None):
859
"""Pull in new perfect-fit revisions."""
1106
"""See Branch.update_revisions."""
860
1107
from bzrlib.fetch import greedy_fetch
861
1108
if stop_revision is None:
862
1109
stop_revision = other.last_revision()