~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Robert Collins
  • Date: 2005-09-30 14:27:41 UTC
  • mto: This revision was merged to the branch mainline in revision 1397.
  • Revision ID: robertc@robertcollins.net-20050930142741-fc326c828b5bbefd
text_version and name_version unification looking reasonable

Show diffs side-by-side

added added

removed removed

Lines of Context:
214
214
            self._check_parents_present()
215
215
            
216
216
            self._remove_deleted()
217
 
            self.new_inv = Inventory()
218
 
            self._store_entries()
 
217
            self._populate_new_inv()
 
218
            self._store_snapshot()
219
219
            self._report_deletes()
220
 
            self._set_revisions()
221
220
 
222
221
            if not (self.allow_pointless
223
222
                    or len(self.parents) > 1
346
345
            self.branch._write_inventory(self.work_inv)
347
346
 
348
347
 
349
 
    def _find_file_parents(self, file_id):
 
348
    def _find_entry_parents(self, file_id):
350
349
        """Return the text versions and hashes for all file parents.
351
350
 
352
351
        Returned as a map from text version to inventory entry.
358
357
        for tree in self.parent_trees:
359
358
            if file_id in tree.inventory:
360
359
                ie = tree.inventory[file_id]
361
 
                assert ie.kind == 'file'
362
360
                assert ie.file_id == file_id
363
 
                if ie.text_version in r:
364
 
                    assert r[ie.text_version] == ie
 
361
                if ie.revision in r:
 
362
                    assert r[ie.revision] == ie
365
363
                else:
366
 
                    r[ie.text_version] = ie
 
364
                    r[ie.revision] = ie
367
365
        return r
368
366
 
369
 
 
370
 
    def _set_revisions(self):
371
 
        """Pass over inventory and mark new revisions as needed.
 
367
    def _snapshot_entry(self, path, ie, previous_entries):
 
368
        """Store a single possibly changed inventory entry in the branch."""
 
369
        mutter('parents of %s are %r', path, previous_entries)
 
370
        if ie.revision is not None:
 
371
            # not selected for commit
 
372
            return
 
373
        if ie.kind == 'symlink':
 
374
            ie.read_symlink_target(self.branch.abspath(path))
 
375
        if len(previous_entries) == 1:
 
376
            # cannot be unchanged unless there is only one parent file rev.
 
377
            parent_ie = previous_entries.values()[0]
 
378
            unchanged = ie.unchanged(parent_ie, self.work_tree)
 
379
            if unchanged:
 
380
                mutter("found unchanged entry")
 
381
                ie.revision = parent_ie.revision
 
382
                self.report_entry_status(previous_entries, path, ie)
 
383
                return 
 
384
        mutter('new revision for {%s}', ie.file_id)
 
385
        ie.revision = self.rev_id
 
386
        if ie.kind != 'file':
 
387
            self.report_entry_status(previous_entries, path, ie)
 
388
            return
 
389
        # file is either new, or a file merge; need to record
 
390
        # a new version
 
391
        self.report_entry_status(previous_entries, path, ie)
 
392
        #if not unchanged:
 
393
        self._commit_file(ie, previous_entries)
 
394
 
 
395
    def report_entry_status(self, previous_entries, path, ie):
 
396
        if len(previous_entries) > 1:
 
397
            note('merged %s', path)
 
398
        elif len(previous_entries) == 0:
 
399
            note('added %s', path)
 
400
        elif ie.revision == self.rev_id:
 
401
            note('modified/renamed/reparented%s', path)
 
402
        else:
 
403
            note('unchanged %s', path)
 
404
 
 
405
    def _store_snapshot(self):
 
406
        """Pass over inventory and record a snapshot.
372
407
 
373
408
        Entries get a new revision when they are modified in 
374
409
        any way, which includes a merge with a new set of
375
410
        parents that have the same entry. Currently we do not
376
411
        check for that set being ancestors of each other - and
377
412
        we should - only parallel children should count for this
378
 
        test. I.e. if we are merging in revision FOO, and our
 
413
        test see find_entry_parents to correct this. FIXME <---
 
414
        I.e. if we are merging in revision FOO, and our
379
415
        copy of file id BAR is identical to FOO.BAR, we should
380
416
        generate a new revision of BAR IF and only IF FOO is
381
417
        neither a child of our current tip, nor an ancestor of
387
423
        # made a specific decision on a particular value -- c.f.
388
424
        # mark-merge.  
389
425
        for path, ie in self.new_inv.iter_entries():
390
 
            compatible_priors = set()
391
 
            file_id = ie.file_id
392
 
            for previous_tree in self.parent_trees:
393
 
                old_version = None
394
 
                previous_inv = previous_tree.inventory
395
 
                if file_id not in previous_inv:
396
 
                    continue
397
 
                previous_ie = previous_inv[file_id]
398
 
                if ie.compatible_for_commit(previous_ie):
399
 
                    mutter("found compatible previous entry")
400
 
                    compatible_priors.add(previous_ie.revision)
401
 
            if len(compatible_priors) != 1:
402
 
                mutter('new revision for {%s}', file_id)
403
 
                ie.revision = self.rev_id
404
 
            else:
405
 
                ie.revision = compatible_priors.pop()
406
 
                mutter('revision for {%s} inherited as {%s}',
407
 
                       file_id, ie.revision)
408
 
 
409
 
    def _store_entries(self):
410
 
        """Build revision inventory and store modified files.
411
 
 
412
 
        This is called with new_inv a new empty inventory.  Depending on
413
 
        which files are selected for commit, and which ones have
414
 
        been modified or merged, new inventory entries are built
415
 
        based on the working and parent inventories.
416
 
 
417
 
        As a side-effect this stores new text versions for committed
418
 
        files with text changes or merges.
419
 
 
420
 
        Each entry can have one of several things happen:
421
 
 
422
 
        carry_file -- carried from the previous version (if not
423
 
            selected for commit)
424
 
 
425
 
        commit_nonfile -- no text to worry about
426
 
 
427
 
        commit_old_text -- same text, may have moved
428
 
 
429
 
        commit_file -- new text version
 
426
            previous_entries = self._find_entry_parents(ie. file_id)
 
427
            self._snapshot_entry(path, ie, previous_entries)
 
428
 
 
429
    def _populate_new_inv(self):
 
430
        """Build revision inventory.
 
431
 
 
432
        This creates a new empty inventory. Depending on
 
433
        which files are selected for commit, and what is present in the
 
434
        current tree, the new inventory is populated. inventory entries 
 
435
        which are candidates for modification have their revision set to
 
436
        None; inventory entries that are carried over untouched have their
 
437
        revision set to their prior value.
430
438
        """
 
439
        mutter("Selecting files for commit with filter %s", self.specific_files)
 
440
        self.new_inv = Inventory()
431
441
        for path, new_ie in self.work_inv.iter_entries():
432
442
            file_id = new_ie.file_id
433
443
            mutter('check %s {%s}', path, new_ie.file_id)
436
446
                    mutter('%s not selected for commit', path)
437
447
                    self._carry_file(file_id)
438
448
                    continue
439
 
            if new_ie.kind == 'symlink':
440
 
                new_ie.read_symlink_target(self.branch.abspath(path))
441
 
            if new_ie.kind != 'file':
442
 
                self._commit_nonfile(file_id)
443
 
                continue
444
 
            
445
 
            file_parents = self._find_file_parents(file_id)
446
 
            mutter('parents of %s are %r', path, file_parents)
447
 
            if len(file_parents) == 1:
448
 
                parent_ie = file_parents.values()[0]
449
 
                wc_sha1 = self.work_tree.get_file_sha1(file_id)
450
 
                if parent_ie.text_sha1 == wc_sha1:
451
 
                    # text not changed or merged
452
 
                    self._commit_old_text(file_id, parent_ie)
453
 
                    continue
454
 
            # file is either new, or a file merge; need to record
455
 
            # a new version
456
 
            if len(file_parents) > 1:
457
 
                note('merged %s', path)
458
 
            elif len(file_parents) == 0:
459
 
                note('added %s', path)
460
 
            else:
461
 
                note('modified %s', path)
462
 
            self._commit_file(new_ie, file_id, file_parents)
463
 
 
464
 
 
465
 
    def _commit_nonfile(self, file_id):
466
 
        self.new_inv.add(self.work_inv[file_id].copy())
467
 
 
 
449
            mutter('%s selected for commit', path)
 
450
            ie = new_ie.copy()
 
451
            ie.revision = None
 
452
            self.new_inv.add(ie)
468
453
 
469
454
    def _carry_file(self, file_id):
470
455
        """Carry the file unchanged from the basis revision."""
471
456
        if self.basis_inv.has_id(file_id):
472
457
            self.new_inv.add(self.basis_inv[file_id].copy())
473
458
 
474
 
 
475
 
    def _commit_old_text(self, file_id, parent_ie):
476
 
        """Keep the same text as last time, but possibly a different name."""
477
 
        ie = self.work_inv[file_id].copy()
478
 
        ie.text_version = parent_ie.text_version
479
 
        ie.text_size = parent_ie.text_size
480
 
        ie.text_sha1 = parent_ie.text_sha1
481
 
        self.new_inv.add(ie)
482
 
 
483
 
 
484
459
    def _report_deletes(self):
485
460
        for file_id in self.basis_inv:
486
461
            if file_id not in self.new_inv:
487
462
                note('deleted %s', self.basis_inv.id2path(file_id))
488
463
 
489
 
 
490
 
    def _commit_file(self, new_ie, file_id, file_parents):                    
491
 
        mutter('store new text for {%s} in revision {%s}',
492
 
               file_id, self.rev_id)
493
 
        new_lines = self.work_tree.get_file(file_id).readlines()
494
 
        self._add_text_to_weave(file_id, new_lines, file_parents)
495
 
        new_ie.text_version = self.rev_id
496
 
        new_ie.text_sha1 = sha_strings(new_lines)
497
 
        new_ie.text_size = sum(map(len, new_lines))
498
 
        self.new_inv.add(new_ie)
499
 
 
 
464
    def _commit_file(self, new_ie, file_parents):                    
 
465
        mutter('storing file {%s} in revision {%s}',
 
466
               new_ie.file_id, new_ie.revision)
 
467
        # special case to avoid diffing on renames or 
 
468
        # reparenting
 
469
        if (len(file_parents) == 1
 
470
            and new_ie.text_sha1 == file_parents.values()[0].text_sha1
 
471
            and new_ie.text_size == file_parents.values()[0].text_size):
 
472
            previous_ie = file_parents.values()[0]
 
473
            self.weave_store.add_identical_text(
 
474
                new_ie.file_id, previous_ie.revision, 
 
475
                new_ie.revision, file_parents)
 
476
        else:
 
477
            new_lines = self.work_tree.get_file(new_ie.file_id).readlines()
 
478
            self._add_text_to_weave(new_ie.file_id, new_lines, file_parents)
 
479
            new_ie.text_sha1 = sha_strings(new_lines)
 
480
            new_ie.text_size = sum(map(len, new_lines))
500
481
 
501
482
    def _add_text_to_weave(self, file_id, new_lines, parents):
502
483
        self.weave_store.add_text(file_id, self.rev_id, new_lines, parents)
503
484
 
504
 
 
505
485
def _gen_revision_id(branch, when):
506
486
    """Return new revision-id."""
507
487
    s = '%s-%s-' % (user_email(branch), compact_date(when))