/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Martin Pool
  • Date: 2006-10-19 02:59:24 UTC
  • mfrom: (2089 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2090.
  • Revision ID: mbp@sourcefrog.net-20061019025924-1af1ed4510b4e71f
merge up 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.
250
 
            if self.work_tree.last_revision() != self.master_branch.last_revision():
 
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
 
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)
252
267
    
253
268
            if strict:
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()
 
302
            
283
303
            self.builder = self.branch.get_commit_builder(self.parents, 
284
304
                self.config, timestamp, timezone, committer, revprops, rev_id)
285
305
            
287
307
            self._populate_new_inv()
288
308
            self._report_deletes()
289
309
 
290
 
            if not (self.allow_pointless
291
 
                    or len(self.parents) > 1
292
 
                    or self.builder.new_inventory != self.basis_inv):
293
 
                raise PointlessCommit()
 
310
            self._check_pointless()
294
311
 
295
312
            self._emit_progress_update()
296
313
            # TODO: Now the new inventory is known, check for conflicts and
317
334
            # and now do the commit locally.
318
335
            self.branch.append_revision(self.rev_id)
319
336
 
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
323
340
            
324
341
            self.reporter.completed(self.branch.revno(), self.rev_id)
335
352
            self._cleanup()
336
353
        return self.rev_id
337
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
 
338
409
    def _check_bound_branch(self):
339
410
        """Check to see if the local branch is bound.
340
411
 
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)
 
515
            else:
 
516
                mutter('commit parent ghost revision {%s}', revision)
443
517
 
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))
452
 
            
453
518
    def _remove_deleted(self):
454
519
        """Remove deleted files from the working inventories.
455
520
 
463
528
        """
464
529
        specific = self.specific_files
465
530
        deleted_ids = []
 
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.
 
535
                continue
467
536
            if specific and not is_inside_any(specific, path):
468
537
                continue
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))
472
 
        if deleted_ids:
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)
477
543
 
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'
514
581
                else:
515
582
                    # this entry is new and not being committed
516
583
                    continue
517
 
 
518
584
            self.builder.record_entry_contents(ie, self.parent_invs, 
519
585
                path, self.work_tree)
520
586
            # describe the nature of the change that has occurred relative to