~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

Merge bzr.dev.

Show diffs side-by-side

added added

removed removed

Lines of Context:
61
61
 
62
62
# TODO: If commit fails, leave the message in a file somewhere.
63
63
 
 
64
# TODO: Change the parameter 'rev_id' to 'revision_id' to be consistent with
 
65
# the rest of the code; add a deprecation of the old name.
64
66
 
65
67
import os
66
68
import re
69
71
 
70
72
from cStringIO import StringIO
71
73
 
 
74
from bzrlib import (
 
75
    errors,
 
76
    tree,
 
77
    )
72
78
import bzrlib.config
73
 
import bzrlib.errors as errors
74
79
from bzrlib.errors import (BzrError, PointlessCommit,
75
80
                           ConflictsInTree,
76
81
                           StrictCommitFailed
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':
123
128
            return
 
129
        if change == 'added' and path == '':
 
130
            return
124
131
        note("%s %s", change, path)
125
132
 
126
133
    def completed(self, revno, rev_id):
245
252
            self._check_bound_branch()
246
253
 
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.
 
255
            try:
 
256
                first_tree_parent = self.work_tree.get_parent_ids()[0]
 
257
            except IndexError:
 
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)
254
267
    
255
268
            if strict:
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
289
307
            self._populate_new_inv()
290
308
            self._report_deletes()
291
309
 
292
 
            if not (self.allow_pointless
293
 
                    or len(self.parents) > 1
294
 
                    or self.builder.new_inventory != self.basis_inv):
295
 
                raise PointlessCommit()
 
310
            self._check_pointless()
296
311
 
297
312
            self._emit_progress_update()
298
313
            # TODO: Now the new inventory is known, check for conflicts and
319
334
            # and now do the commit locally.
320
335
            self.branch.append_revision(self.rev_id)
321
336
 
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
328
340
            
329
341
            self.reporter.completed(self.branch.revno(), self.rev_id)
340
352
            self._cleanup()
341
353
        return self.rev_id
342
354
 
 
355
    def _any_real_changes(self):
 
356
        """Are there real changes between new_inventory and basis?
 
357
 
 
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
 
360
        are *no* changes.
 
361
        """
 
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()
 
366
 
 
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)
 
378
                    )
 
379
        if not ie_equal_no_revision(new_root_ie, basis_root_ie):
 
380
            return True
 
381
 
 
382
        for new_ie, basis_ie in zip(new_entries, basis_entries):
 
383
            if new_ie != basis_ie:
 
384
                return True
 
385
 
 
386
        # No actual changes present
 
387
        return False
 
388
 
 
389
    def _check_pointless(self):
 
390
        if self.allow_pointless:
 
391
            return
 
392
        # A merge with no effect on files
 
393
        if len(self.parents) > 1:
 
394
            return
 
395
        # work around the fact that a newly-initted tree does differ from its
 
396
        # basis
 
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
 
400
        # a change
 
401
        if len(self.builder.new_inventory) != len(self.basis_inv):
 
402
            return
 
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()):
 
406
            return
 
407
        raise PointlessCommit()
 
408
 
343
409
    def _check_bound_branch(self):
344
410
        """Check to see if the local branch is bound.
345
411
 
462
528
        """
463
529
        specific = self.specific_files
464
530
        deleted_ids = []
 
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.
 
535
                continue
466
536
            if specific and not is_inside_any(specific, path):
467
537
                continue
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))
471
 
        if deleted_ids:
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)
476
543
 
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'
513
581
                else:
514
582
                    # this entry is new and not being committed
515
583
                    continue
516
 
 
517
584
            self.builder.record_entry_contents(ie, self.parent_invs, 
518
585
                path, self.work_tree)
519
586
            # describe the nature of the change that has occurred relative to