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.
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
250
263
master_last = self.master_branch.last_revision()
251
if (master_last is not None and
252
master_last != self.work_tree.last_revision()):
264
if (master_last is not None and
265
master_last != first_tree_parent):
253
266
raise errors.OutOfDateTree(self.work_tree)
269
282
self.work_inv = self.work_tree.inventory
270
283
self.basis_tree = self.work_tree.basis_tree()
271
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])
272
290
# one to finish, one for rev and inventory, and one for each
273
291
# inventory entry, and the same for the new inventory.
274
292
# note that this estimate is too long when we do a partial tree
319
334
# and now do the commit locally.
320
335
self.branch.append_revision(self.rev_id)
322
# if the builder gave us the revisiontree it created back, we
323
# could use it straight away here.
324
# TODO: implement this.
325
self.work_tree.set_parent_trees([(self.rev_id,
326
self.branch.repository.revision_tree(self.rev_id))])
337
rev_tree = self.builder.revision_tree()
338
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
327
339
# now the work tree is up to date with the branch
329
341
self.reporter.completed(self.branch.revno(), self.rev_id)
341
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()
343
409
def _check_bound_branch(self):
344
410
"""Check to see if the local branch is bound.
463
529
specific = self.specific_files
531
deleted_paths = set()
465
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.
466
536
if specific and not is_inside_any(specific, path):
468
538
if not self.work_tree.has_filename(path):
539
deleted_paths.add(path)
469
540
self.reporter.missing(path)
470
deleted_ids.append((path, ie.file_id))
472
deleted_ids.sort(reverse=True)
473
for path, file_id in deleted_ids:
474
del self.work_inv[file_id]
475
self.work_tree._write_inventory(self.work_inv)
541
deleted_ids.append(ie.file_id)
542
self.work_tree.unversion(deleted_ids)
477
544
def _populate_new_inv(self):
478
545
"""Build revision inventory.
489
556
# in bugs like #46635. Any reason not to use/enhance Tree.changes_from?
490
557
# ADHB 11-07-2006
491
558
mutter("Selecting files for commit with filter %s", self.specific_files)
559
assert self.work_inv.root is not None
492
560
entries = self.work_inv.iter_entries()
493
561
if not self.builder.record_root_entry:
494
562
symbol_versioning.warn('CommitBuilders should support recording'