70
72
from cStringIO import StringIO
72
79
import bzrlib.config
73
from bzrlib import errors, inventory
74
80
from bzrlib.errors import (BzrError, PointlessCommit,
245
251
self._check_bound_branch()
247
253
# 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():
255
first_tree_parent = self.work_tree.get_parent_ids()[0]
257
# if there are no parents, treat our parent as 'None'
258
# this is so that we still consier the master branch
259
# - in a checkout scenario the tree may have no
260
# parents but the branch may do.
261
first_tree_parent = None
262
master_last = self.master_branch.last_revision()
263
if (master_last is not None and
264
master_last != first_tree_parent):
251
265
raise errors.OutOfDateTree(self.work_tree)
267
281
self.work_inv = self.work_tree.inventory
268
282
self.basis_tree = self.work_tree.basis_tree()
269
283
self.basis_inv = self.basis_tree.inventory
284
if specific_files is not None:
285
# Ensure specified files are versioned
286
# (We don't actually need the ids here)
287
tree.find_ids_across_trees(specific_files,
288
[self.basis_tree, self.work_tree])
270
289
# one to finish, one for rev and inventory, and one for each
271
290
# inventory entry, and the same for the new inventory.
272
291
# note that this estimate is too long when we do a partial tree
279
298
if len(self.parents) > 1 and self.specific_files:
280
299
raise NotImplementedError('selected-file commit of merges is not supported yet: files %r',
281
300
self.specific_files)
282
self._check_parents_present()
283
302
self.builder = self.branch.get_commit_builder(self.parents,
284
303
self.config, timestamp, timezone, committer, revprops, rev_id)
287
306
self._populate_new_inv()
288
307
self._report_deletes()
290
if not (self.allow_pointless
291
or len(self.parents) > 1
292
or self.builder.new_inventory != self.basis_inv):
293
raise PointlessCommit()
309
self._check_pointless()
295
311
self._emit_progress_update()
296
312
# TODO: Now the new inventory is known, check for conflicts and
317
333
# and now do the commit locally.
318
334
self.branch.append_revision(self.rev_id)
320
self.work_tree.set_pending_merges([])
321
self.work_tree.set_last_revision(self.rev_id)
336
rev_tree = self.builder.revision_tree()
337
self.work_tree.set_parent_trees([(self.rev_id, rev_tree)])
322
338
# now the work tree is up to date with the branch
324
340
self.reporter.completed(self.branch.revno(), self.rev_id)
336
352
return self.rev_id
354
def _any_real_changes(self):
355
"""Are there real changes between new_inventory and basis?
357
For trees without rich roots, inv.root.revision changes every commit.
358
But if that is the only change, we want to treat it as though there
361
new_entries = self.builder.new_inventory.iter_entries()
362
basis_entries = self.basis_inv.iter_entries()
363
new_path, new_root_ie = new_entries.next()
364
basis_path, basis_root_ie = basis_entries.next()
366
# This is a copy of InventoryEntry.__eq__ only leaving out .revision
367
def ie_equal_no_revision(this, other):
368
return ((this.file_id == other.file_id)
369
and (this.name == other.name)
370
and (this.symlink_target == other.symlink_target)
371
and (this.text_sha1 == other.text_sha1)
372
and (this.text_size == other.text_size)
373
and (this.text_id == other.text_id)
374
and (this.parent_id == other.parent_id)
375
and (this.kind == other.kind)
376
and (this.executable == other.executable)
378
if not ie_equal_no_revision(new_root_ie, basis_root_ie):
381
for new_ie, basis_ie in zip(new_entries, basis_entries):
382
if new_ie != basis_ie:
385
# No actual changes present
388
def _check_pointless(self):
389
if self.allow_pointless:
391
# A merge with no effect on files
392
if len(self.parents) > 1:
394
# Shortcut, if the number of entries changes, then we obviously have
396
if len(self.builder.new_inventory) != len(self.basis_inv):
398
# If length == 1, then we only have the root entry. Which means
399
# that there is no real difference (only the root could be different)
400
if (len(self.builder.new_inventory) != 1 and self._any_real_changes()):
402
raise PointlessCommit()
338
404
def _check_bound_branch(self):
339
405
"""Check to see if the local branch is bound.
438
504
self.parent_invs = []
439
505
for revision in self.parents:
440
506
if self.branch.repository.has_revision(revision):
507
mutter('commit parent revision {%s}', revision)
441
508
inventory = self.branch.repository.get_inventory(revision)
442
509
self.parent_invs.append(inventory)
511
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
513
def _remove_deleted(self):
454
514
"""Remove deleted files from the working inventories.
464
524
specific = self.specific_files
526
deleted_paths = set()
466
527
for path, ie in self.work_inv.iter_entries():
528
if is_inside_any(deleted_paths, path):
529
# The tree will delete the required ids recursively.
467
531
if specific and not is_inside_any(specific, path):
469
533
if not self.work_tree.has_filename(path):
534
deleted_paths.add(path)
470
535
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)
536
deleted_ids.append(ie.file_id)
537
self.work_tree.unversion(deleted_ids)
478
539
def _populate_new_inv(self):
479
540
"""Build revision inventory.
501
562
for path, new_ie in entries:
502
563
self._emit_progress_update()
503
564
file_id = new_ie.file_id
504
kind = self.work_tree.kind(file_id)
505
if kind != new_ie.kind:
506
new_ie = inventory.make_entry(kind, new_ie.name,
507
new_ie.parent_id, file_id)
566
kind = self.work_tree.kind(file_id)
567
if kind != new_ie.kind:
568
new_ie = inventory.make_entry(kind, new_ie.name,
569
new_ie.parent_id, file_id)
570
except errors.NoSuchFile:
508
572
# mutter('check %s {%s}', path, file_id)
509
573
if (not self.specific_files or
510
574
is_inside_or_parent_of_any(self.specific_files, path)):