81
86
from bzrlib.testament import Testament
82
87
from bzrlib.trace import mutter, note, warning
83
88
from bzrlib.xml5 import serializer_v5
84
from bzrlib.inventory import Inventory, ROOT_ID, InventoryEntry
89
from bzrlib.inventory import Inventory, InventoryEntry
85
90
from bzrlib import symbol_versioning
86
91
from bzrlib.symbol_versioning import (deprecated_passed,
87
92
deprecated_function,
121
126
def snapshot_change(self, change, path):
122
127
if change == 'unchanged':
129
if change == 'added' and path == '':
124
131
note("%s %s", change, path)
126
133
def completed(self, revno, rev_id):
245
252
self._check_bound_branch()
247
254
# check for out of date working trees
248
# if we are bound, then self.branch is the master branch and this
249
# test is thus all we need.
250
if self.work_tree.last_revision() != self.master_branch.last_revision():
256
first_tree_parent = self.work_tree.get_parent_ids()[0]
258
# if there are no parents, treat our parent as 'None'
259
# this is so that we still consier the master branch
260
# - in a checkout scenario the tree may have no
261
# parents but the branch may do.
262
first_tree_parent = None
263
master_last = self.master_branch.last_revision()
264
if (master_last is not None and
265
master_last != first_tree_parent):
251
266
raise errors.OutOfDateTree(self.work_tree)
267
282
self.work_inv = self.work_tree.inventory
268
283
self.basis_tree = self.work_tree.basis_tree()
269
284
self.basis_inv = self.basis_tree.inventory
285
if specific_files is not None:
286
# Ensure specified files are versioned
287
# (We don't actually need the ids here)
288
tree.find_ids_across_trees(specific_files,
289
[self.basis_tree, self.work_tree])
270
290
# one to finish, one for rev and inventory, and one for each
271
291
# inventory entry, and the same for the new inventory.
272
292
# note that this estimate is too long when we do a partial tree
279
299
if len(self.parents) > 1 and self.specific_files:
280
300
raise NotImplementedError('selected-file commit of merges is not supported yet: files %r',
281
301
self.specific_files)
282
self._check_parents_present()
283
303
self.builder = self.branch.get_commit_builder(self.parents,
284
304
self.config, timestamp, timezone, committer, revprops, rev_id)
317
334
# and now do the commit locally.
318
335
self.branch.append_revision(self.rev_id)
320
self.work_tree.set_pending_merges([])
321
self.work_tree.set_last_revision(self.rev_id)
337
rev_tree = self.builder.revision_tree()
338
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
322
339
# now the work tree is up to date with the branch
324
341
self.reporter.completed(self.branch.revno(), self.rev_id)
336
353
return self.rev_id
355
def _any_real_changes(self):
356
"""Are there real changes between new_inventory and basis?
358
For trees without rich roots, inv.root.revision changes every commit.
359
But if that is the only change, we want to treat it as though there
362
new_entries = self.builder.new_inventory.iter_entries()
363
basis_entries = self.basis_inv.iter_entries()
364
new_path, new_root_ie = new_entries.next()
365
basis_path, basis_root_ie = basis_entries.next()
367
# This is a copy of InventoryEntry.__eq__ only leaving out .revision
368
def ie_equal_no_revision(this, other):
369
return ((this.file_id == other.file_id)
370
and (this.name == other.name)
371
and (this.symlink_target == other.symlink_target)
372
and (this.text_sha1 == other.text_sha1)
373
and (this.text_size == other.text_size)
374
and (this.text_id == other.text_id)
375
and (this.parent_id == other.parent_id)
376
and (this.kind == other.kind)
377
and (this.executable == other.executable)
379
if not ie_equal_no_revision(new_root_ie, basis_root_ie):
382
for new_ie, basis_ie in zip(new_entries, basis_entries):
383
if new_ie != basis_ie:
386
# No actual changes present
389
def _check_pointless(self):
390
if self.allow_pointless:
392
# A merge with no effect on files
393
if len(self.parents) > 1:
395
# work around the fact that a newly-initted tree does differ from its
397
if len(self.basis_inv) == 0 and len(self.builder.new_inventory) == 1:
398
raise PointlessCommit()
399
# Shortcut, if the number of entries changes, then we obviously have
401
if len(self.builder.new_inventory) != len(self.basis_inv):
403
# If length == 1, then we only have the root entry. Which means
404
# that there is no real difference (only the root could be different)
405
if (len(self.builder.new_inventory) != 1 and self._any_real_changes()):
407
raise PointlessCommit()
338
409
def _check_bound_branch(self):
339
410
"""Check to see if the local branch is bound.
438
509
self.parent_invs = []
439
510
for revision in self.parents:
440
511
if self.branch.repository.has_revision(revision):
512
mutter('commit parent revision {%s}', revision)
441
513
inventory = self.branch.repository.get_inventory(revision)
442
514
self.parent_invs.append(inventory)
516
mutter('commit parent ghost revision {%s}', revision)
444
def _check_parents_present(self):
445
for parent_id in self.parents:
446
mutter('commit parent revision {%s}', parent_id)
447
if not self.branch.repository.has_revision(parent_id):
448
if parent_id == self.branch.last_revision():
449
warning("parent is missing %r", parent_id)
450
raise BzrCheckError("branch %s is missing revision {%s}"
451
% (self.branch, parent_id))
453
518
def _remove_deleted(self):
454
519
"""Remove deleted files from the working inventories.
464
529
specific = self.specific_files
531
deleted_paths = set()
466
532
for path, ie in self.work_inv.iter_entries():
533
if is_inside_any(deleted_paths, path):
534
# The tree will delete the required ids recursively.
467
536
if specific and not is_inside_any(specific, path):
469
538
if not self.work_tree.has_filename(path):
539
deleted_paths.add(path)
470
540
self.reporter.missing(path)
471
deleted_ids.append((path, ie.file_id))
473
deleted_ids.sort(reverse=True)
474
for path, file_id in deleted_ids:
475
del self.work_inv[file_id]
476
self.work_tree._write_inventory(self.work_inv)
541
deleted_ids.append(ie.file_id)
542
self.work_tree.unversion(deleted_ids)
478
544
def _populate_new_inv(self):
479
545
"""Build revision inventory.
490
556
# in bugs like #46635. Any reason not to use/enhance Tree.changes_from?
491
557
# ADHB 11-07-2006
492
558
mutter("Selecting files for commit with filter %s", self.specific_files)
559
assert self.work_inv.root is not None
493
560
entries = self.work_inv.iter_entries()
494
561
if not self.builder.record_root_entry:
495
562
symbol_versioning.warn('CommitBuilders should support recording'