/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-06-20 03:30:14 UTC
  • mfrom: (1793 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1797.
  • Revision ID: mbp@sourcefrog.net-20060620033014-e19ce470e2ce6561
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
69
69
import time
70
70
import pdb
71
71
 
72
 
from binascii import hexlify
73
72
from cStringIO import StringIO
74
73
 
75
74
from bzrlib.atomicfile import AtomicFile
76
 
from bzrlib.osutils import (local_time_offset,
77
 
                            rand_bytes, compact_date,
78
 
                            kind_marker, is_inside_any, quotefn,
79
 
                            sha_file, isdir, isfile,
80
 
                            split_lines)
81
75
import bzrlib.config
82
76
import bzrlib.errors as errors
83
77
from bzrlib.errors import (BzrError, PointlessCommit,
84
78
                           ConflictsInTree,
85
79
                           StrictCommitFailed
86
80
                           )
87
 
from bzrlib.revision import Revision
 
81
from bzrlib.osutils import (kind_marker, isdir,isfile, is_inside_any, 
 
82
                            is_inside_or_parent_of_any,
 
83
                            quotefn, sha_file, split_lines)
88
84
from bzrlib.testament import Testament
89
85
from bzrlib.trace import mutter, note, warning
90
86
from bzrlib.xml5 import serializer_v5
101
97
 
102
98
    New code should use the Commit class instead.
103
99
    """
104
 
    ## XXX: Remove this in favor of Branch.commit?
 
100
    ## XXX: Remove this in favor of WorkingTree.commit?
105
101
    Commit().commit(*args, **kwargs)
106
102
 
107
103
 
237
233
        if message is None:
238
234
            raise BzrError("The message keyword parameter is required for commit().")
239
235
 
240
 
        self.weave_store = self.branch.repository.weave_store
241
236
        self.bound_branch = None
242
237
        self.local = local
243
238
        self.master_branch = None
244
239
        self.master_locked = False
245
 
        self.rev_id = rev_id
 
240
        self.rev_id = None
246
241
        self.specific_files = specific_files
247
242
        self.allow_pointless = allow_pointless
248
 
        self.revprops = {}
249
 
        if revprops is not None:
250
 
            self.revprops.update(revprops)
251
243
 
252
244
        if reporter is None and self.reporter is None:
253
245
            self.reporter = NullCommitReporter()
274
266
                # raise an exception as soon as we find a single unknown.
275
267
                for unknown in self.work_tree.unknowns():
276
268
                    raise StrictCommitFailed()
277
 
    
278
 
            if timestamp is None:
279
 
                self.timestamp = time.time()
280
 
            else:
281
 
                self.timestamp = long(timestamp)
282
 
                
 
269
                   
283
270
            if self.config is None:
284
271
                self.config = bzrlib.config.BranchConfig(self.branch)
285
 
    
286
 
            if rev_id is None:
287
 
                self.rev_id = _gen_revision_id(self.config, self.timestamp)
288
 
            else:
289
 
                self.rev_id = rev_id
290
 
    
291
 
            if committer is None:
292
 
                self.committer = self.config.username()
293
 
            else:
294
 
                assert isinstance(committer, basestring), type(committer)
295
 
                self.committer = committer
296
 
    
297
 
            if timezone is None:
298
 
                self.timezone = local_time_offset()
299
 
            else:
300
 
                self.timezone = int(timezone)
301
 
    
 
272
      
302
273
            if isinstance(message, str):
303
274
                message = message.decode(bzrlib.user_encoding)
304
275
            assert isinstance(message, unicode), type(message)
313
284
            # note that this estimate is too long when we do a partial tree
314
285
            # commit which excludes some new files from being considered.
315
286
            # The estimate is corrected when we populate the new inv.
316
 
            self.pb_total = len(self.basis_inv) + len(self.work_inv) + 3 - 1
 
287
            self.pb_total = len(self.work_inv) + 5
317
288
            self.pb_count = 0
318
289
 
319
290
            self._gather_parents()
321
292
                raise NotImplementedError('selected-file commit of merges is not supported yet: files %r',
322
293
                        self.specific_files)
323
294
            self._check_parents_present()
 
295
            self.builder = self.branch.get_commit_builder(self.parents, 
 
296
                self.config, timestamp, timezone, committer, revprops, rev_id)
324
297
            
325
298
            self._remove_deleted()
326
299
            self._populate_new_inv()
327
 
            self._store_snapshot()
328
300
            self._report_deletes()
329
301
 
330
302
            if not (self.allow_pointless
331
303
                    or len(self.parents) > 1
332
 
                    or self.new_inv != self.basis_inv):
 
304
                    or self.builder.new_inventory != self.basis_inv):
333
305
                raise PointlessCommit()
334
306
 
335
307
            self._emit_progress_update()
336
 
            self.inv_sha1 = self.branch.repository.add_inventory(
337
 
                self.rev_id,
338
 
                self.new_inv,
339
 
                self.present_parents
340
 
                )
341
 
            self._emit_progress_update()
342
 
            self._make_revision()
 
308
            # TODO: Now the new inventory is known, check for conflicts and prompt the 
 
309
            # user for a commit message.
 
310
            self.builder.finish_inventory()
 
311
            self._emit_progress_update()
 
312
            self.rev_id = self.builder.commit(self.message)
 
313
            self._emit_progress_update()
343
314
            # revision data is in the local branch now.
344
315
            
345
316
            # upload revision data to the master.
346
 
            # this will propogate merged revisions too if needed.
 
317
            # this will propagate merged revisions too if needed.
347
318
            if self.bound_branch:
348
319
                self.master_branch.repository.fetch(self.branch.repository,
349
320
                                                    revision_id=self.rev_id)
371
342
            self._emit_progress_update()
372
343
        finally:
373
344
            self._cleanup()
 
345
        return self.rev_id
374
346
 
375
347
    def _check_bound_branch(self):
376
348
        """Check to see if the local branch is bound.
413
385
        self.bound_branch = self.branch
414
386
        self.master_branch.lock_write()
415
387
        self.master_locked = True
416
 
####        
417
 
####        # Check to see if we have any pending merges. If we do
418
 
####        # those need to be pushed into the master branch
419
 
####        pending_merges = self.work_tree.pending_merges()
420
 
####        if pending_merges:
421
 
####            for revision_id in pending_merges:
422
 
####                self.master_branch.repository.fetch(self.bound_branch.repository,
423
 
####                                                    revision_id=revision_id)
424
388
 
425
389
    def _cleanup(self):
426
390
        """Cleanup any open locks, progress bars etc."""
438
402
            except Exception, e:
439
403
                found_exception = e
440
404
        if found_exception is not None: 
441
 
            # dont do a plan raise, because the last exception may have been
 
405
            # don't do a plan raise, because the last exception may have been
442
406
            # trashed, e is our sure-to-work exception even though it loses the
443
407
            # full traceback. XXX: RBC 20060421 perhaps we could check the
444
408
            # exc_info and if its the same one do a plain raise otherwise 
477
441
 
478
442
    def _gather_parents(self):
479
443
        """Record the parents of a merge for merge detection."""
480
 
        pending_merges = self.work_tree.pending_merges()
481
 
        self.parents = []
 
444
        # TODO: Make sure that this list doesn't contain duplicate 
 
445
        # entries and the order is preserved when doing this.
 
446
        self.parents = self.work_tree.get_parent_ids()
482
447
        self.parent_invs = []
483
 
        self.present_parents = []
484
 
        precursor_id = self.branch.last_revision()
485
 
        if precursor_id:
486
 
            self.parents.append(precursor_id)
487
 
        self.parents += pending_merges
488
448
        for revision in self.parents:
489
449
            if self.branch.repository.has_revision(revision):
490
450
                inventory = self.branch.repository.get_inventory(revision)
491
451
                self.parent_invs.append(inventory)
492
 
                self.present_parents.append(revision)
493
452
 
494
453
    def _check_parents_present(self):
495
454
        for parent_id in self.parents:
500
459
                    raise BzrCheckError("branch %s is missing revision {%s}"
501
460
                            % (self.branch, parent_id))
502
461
            
503
 
    def _make_revision(self):
504
 
        """Record a new revision object for this commit."""
505
 
        rev = Revision(timestamp=self.timestamp,
506
 
                       timezone=self.timezone,
507
 
                       committer=self.committer,
508
 
                       message=self.message,
509
 
                       inventory_sha1=self.inv_sha1,
510
 
                       revision_id=self.rev_id,
511
 
                       properties=self.revprops)
512
 
        rev.parent_ids = self.parents
513
 
        self.branch.repository.add_revision(self.rev_id, rev, self.new_inv, self.config)
514
 
 
515
462
    def _remove_deleted(self):
516
463
        """Remove deleted files from the working inventories.
517
464
 
537
484
                del self.work_inv[file_id]
538
485
            self.work_tree._write_inventory(self.work_inv)
539
486
 
540
 
    def _store_snapshot(self):
541
 
        """Pass over inventory and record a snapshot.
542
 
 
543
 
        Entries get a new revision when they are modified in 
544
 
        any way, which includes a merge with a new set of
545
 
        parents that have the same entry. 
546
 
        """
547
 
        # XXX: Need to think more here about when the user has
548
 
        # made a specific decision on a particular value -- c.f.
549
 
        # mark-merge.  
550
 
 
551
 
        # iter_entries does not visit the ROOT_ID node so we need to call
552
 
        # self._emit_progress_update once by hand.
553
 
        self._emit_progress_update()
554
 
        for path, ie in self.new_inv.iter_entries():
555
 
            self._emit_progress_update()
556
 
            previous_entries = ie.find_previous_heads(
557
 
                self.parent_invs,
558
 
                self.weave_store,
559
 
                self.branch.repository.get_transaction())
560
 
            if ie.revision is None:
561
 
                # we are creating a new revision for ie in the history store
562
 
                # and inventory.
563
 
                ie.snapshot(self.rev_id, path, previous_entries,
564
 
                    self.work_tree, self.weave_store,
565
 
                    self.branch.repository.get_transaction())
566
 
            # describe the nature of the change that has occured relative to
567
 
            # the basis inventory.
568
 
            if (self.basis_inv.has_id(ie.file_id)):
569
 
                basis_ie = self.basis_inv[ie.file_id]
570
 
            else:
571
 
                basis_ie = None
572
 
            change = ie.describe_change(basis_ie, ie)
573
 
            if change in (InventoryEntry.RENAMED, 
574
 
                InventoryEntry.MODIFIED_AND_RENAMED):
575
 
                old_path = self.basis_inv.id2path(ie.file_id)
576
 
                self.reporter.renamed(change, old_path, path)
577
 
            else:
578
 
                self.reporter.snapshot_change(change, path)
579
 
 
580
487
    def _populate_new_inv(self):
581
488
        """Build revision inventory.
582
489
 
588
495
        revision set to their prior value.
589
496
        """
590
497
        mutter("Selecting files for commit with filter %s", self.specific_files)
591
 
        self.new_inv = Inventory(revision_id=self.rev_id)
592
498
        # iter_entries does not visit the ROOT_ID node so we need to call
593
499
        # self._emit_progress_update once by hand.
594
500
        self._emit_progress_update()
595
501
        for path, new_ie in self.work_inv.iter_entries():
596
502
            self._emit_progress_update()
597
503
            file_id = new_ie.file_id
598
 
            mutter('check %s {%s}', path, new_ie.file_id)
599
 
            if self.specific_files:
600
 
                if not is_inside_any(self.specific_files, path):
601
 
                    mutter('%s not selected for commit', path)
602
 
                    self._carry_entry(file_id)
 
504
            mutter('check %s {%s}', path, file_id)
 
505
            if (not self.specific_files or 
 
506
                is_inside_or_parent_of_any(self.specific_files, path)):
 
507
                    mutter('%s selected for commit', path)
 
508
                    ie = new_ie.copy()
 
509
                    ie.revision = None
 
510
            else:
 
511
                mutter('%s not selected for commit', path)
 
512
                if self.basis_inv.has_id(file_id):
 
513
                    ie = self.basis_inv[file_id].copy()
 
514
                else:
 
515
                    # this entry is new and not being committed
603
516
                    continue
604
 
                else:
605
 
                    # this is selected, ensure its parents are too.
606
 
                    parent_id = new_ie.parent_id
607
 
                    while parent_id != ROOT_ID:
608
 
                        if not self.new_inv.has_id(parent_id):
609
 
                            ie = self._select_entry(self.work_inv[parent_id])
610
 
                            mutter('%s selected for commit because of %s',
611
 
                                   self.new_inv.id2path(parent_id), path)
612
517
 
613
 
                        ie = self.new_inv[parent_id]
614
 
                        if ie.revision is not None:
615
 
                            ie.revision = None
616
 
                            mutter('%s selected for commit because of %s',
617
 
                                   self.new_inv.id2path(parent_id), path)
618
 
                        parent_id = ie.parent_id
619
 
            mutter('%s selected for commit', path)
620
 
            self._select_entry(new_ie)
 
518
            self.builder.record_entry_contents(ie, self.parent_invs, 
 
519
                path, self.work_tree)
 
520
            # describe the nature of the change that has occurred relative to
 
521
            # the basis inventory.
 
522
            if (self.basis_inv.has_id(ie.file_id)):
 
523
                basis_ie = self.basis_inv[ie.file_id]
 
524
            else:
 
525
                basis_ie = None
 
526
            change = ie.describe_change(basis_ie, ie)
 
527
            if change in (InventoryEntry.RENAMED, 
 
528
                InventoryEntry.MODIFIED_AND_RENAMED):
 
529
                old_path = self.basis_inv.id2path(ie.file_id)
 
530
                self.reporter.renamed(change, old_path, path)
 
531
            else:
 
532
                self.reporter.snapshot_change(change, path)
621
533
 
622
534
    def _emit_progress_update(self):
623
535
        """Emit an update to the progress bar."""
624
536
        self.pb.update("Committing", self.pb_count, self.pb_total)
625
537
        self.pb_count += 1
626
538
 
627
 
    def _select_entry(self, new_ie):
628
 
        """Make new_ie be considered for committing."""
629
 
        ie = new_ie.copy()
630
 
        ie.revision = None
631
 
        self.new_inv.add(ie)
632
 
        return ie
633
 
 
634
 
    def _carry_entry(self, file_id):
635
 
        """Carry the file unchanged from the basis revision."""
636
 
        if self.basis_inv.has_id(file_id):
637
 
            self.new_inv.add(self.basis_inv[file_id].copy())
638
 
        else:
639
 
            # this entry is new and not being committed
640
 
            self.pb_total -= 1
641
 
 
642
539
    def _report_deletes(self):
643
540
        for path, ie in self.basis_inv.iter_entries():
644
 
            if ie.file_id not in self.new_inv:
 
541
            if ie.file_id not in self.builder.new_inventory:
645
542
                self.reporter.deleted(path)
646
543
 
647
 
def _gen_revision_id(config, when):
648
 
    """Return new revision-id."""
649
 
    s = '%s-%s-' % (config.user_email(), compact_date(when))
650
 
    s += hexlify(rand_bytes(8))
651
 
    return s
 
544