~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commit.py

  • Committer: Aaron Bentley
  • Date: 2005-10-03 19:50:36 UTC
  • mfrom: (1399)
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1419.
  • Revision ID: abentley@panoramicfeedback.com-20051003195036-28dbd56f0e852b08
Merged latest from Robert Collins

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_name_versions()
221
220
 
222
221
            if not (self.allow_pointless
223
222
                    or len(self.parents) > 1
234
233
        finally:
235
234
            self.branch.unlock()
236
235
 
237
 
 
238
 
 
239
236
    def _record_inventory(self):
240
237
        """Store the inventory for the new revision."""
241
238
        inv_text = serializer_v5.write_inventory_to_string(self.new_inv)
242
239
        self.inv_sha1 = sha_string(inv_text)
243
240
        s = self.branch.control_weaves
244
241
        s.add_text('inventory', self.rev_id,
245
 
                   split_lines(inv_text), self.parents)
 
242
                   split_lines(inv_text), self.present_parents)
246
243
 
247
244
    def _escape_commit_message(self):
248
245
        """Replace xml-incompatible control characters."""
271
268
        s = self.branch.control_weaves
272
269
        w = s.get_weave_or_empty('ancestry')
273
270
        lines = self._make_ancestry(w)
274
 
        w.add(self.rev_id, self.parents, lines)
 
271
        w.add(self.rev_id, self.present_parents, lines)
275
272
        s.put_weave('ancestry', w)
276
273
 
277
 
 
278
274
    def _make_ancestry(self, ancestry_weave):
279
275
        """Return merged ancestry lines.
280
276
 
281
277
        The lines are revision-ids followed by newlines."""
282
 
        parent_ancestries = [ancestry_weave.get(p) for p in self.parents]
 
278
        parent_ancestries = [ancestry_weave.get(p) for p in self.present_parents]
283
279
        new_lines = merge_ancestry_lines(self.rev_id, parent_ancestries)
284
280
        mutter('merged ancestry of {%s}:\n%s', self.rev_id, ''.join(new_lines))
285
281
        return new_lines
286
282
 
287
 
 
288
283
    def _gather_parents(self):
 
284
        """Record the parents of a merge for merge detection."""
289
285
        pending_merges = self.branch.pending_merges()
290
286
        self.parents = []
291
287
        self.parent_trees = []
 
288
        self.present_parents = []
292
289
        precursor_id = self.branch.last_revision()
293
290
        if precursor_id:
294
291
            self.parents.append(precursor_id)
295
 
            self.parent_trees.append(self.basis_tree)
296
292
        self.parents += pending_merges
297
 
        self.parent_trees.extend(map(self.branch.revision_tree, pending_merges))
298
 
 
 
293
        for revision in self.parents:
 
294
            if self.branch.has_revision(revision):
 
295
                self.parent_trees.append(self.branch.revision_tree(revision))
 
296
                self.present_parents.append(revision)
299
297
 
300
298
    def _check_parents_present(self):
301
299
        for parent_id in self.parents:
302
300
            mutter('commit parent revision {%s}', parent_id)
303
301
            if not self.branch.has_revision(parent_id):
304
 
                warning("can't commit a merge from an absent parent")
305
 
                raise HistoryMissing(self.branch, 'revision', parent_id)
306
 
 
 
302
                if parent_id == self.branch.last_revision():
 
303
                    warning("parent is pissing %r", parent_id)
 
304
                    raise HistoryMissing(self.branch, 'revision', parent_id)
 
305
                else:
 
306
                    mutter("commit will ghost revision %r", parent_id)
307
307
            
308
308
    def _make_revision(self):
309
309
        """Record a new revision object for this commit."""
317
317
        rev_tmp = StringIO()
318
318
        serializer_v5.write_revision(self.rev, rev_tmp)
319
319
        rev_tmp.seek(0)
320
 
        self.branch.revision_store.add(rev_tmp, self.rev_id, compressed=True)
 
320
        self.branch.revision_store.add(rev_tmp, self.rev_id)
321
321
        mutter('new revision_id is {%s}', self.rev_id)
322
322
 
323
323
 
347
347
            self.branch._write_inventory(self.work_inv)
348
348
 
349
349
 
350
 
    def _find_file_parents(self, file_id):
 
350
    def _find_entry_parents(self, file_id):
351
351
        """Return the text versions and hashes for all file parents.
352
352
 
353
353
        Returned as a map from text version to inventory entry.
359
359
        for tree in self.parent_trees:
360
360
            if file_id in tree.inventory:
361
361
                ie = tree.inventory[file_id]
362
 
                assert ie.kind == 'file'
363
362
                assert ie.file_id == file_id
364
 
                if ie.text_version in r:
365
 
                    assert r[ie.text_version] == ie
 
363
                if ie.revision in r:
 
364
                    assert r[ie.revision] == ie
366
365
                else:
367
 
                    r[ie.text_version] = ie
 
366
                    r[ie.revision] = ie
368
367
        return r
369
368
 
370
 
 
371
 
    def _set_name_versions(self):
372
 
        """Pass over inventory and mark new entry version as needed.
373
 
 
374
 
        Files get a new name version when they are new, have a
375
 
        different parent, or a different name from in the
376
 
        basis inventory, or if the file is in a different place
377
 
        to any of the parents."""
 
369
    def _store_snapshot(self):
 
370
        """Pass over inventory and record a snapshot.
 
371
 
 
372
        Entries get a new revision when they are modified in 
 
373
        any way, which includes a merge with a new set of
 
374
        parents that have the same entry. Currently we do not
 
375
        check for that set being ancestors of each other - and
 
376
        we should - only parallel children should count for this
 
377
        test see find_entry_parents to correct this. FIXME <---
 
378
        I.e. if we are merging in revision FOO, and our
 
379
        copy of file id BAR is identical to FOO.BAR, we should
 
380
        generate a new revision of BAR IF and only IF FOO is
 
381
        neither a child of our current tip, nor an ancestor of
 
382
        our tip. The presence of FOO in our store should not 
 
383
        affect this logic UNLESS we are doing a merge of FOO,
 
384
        or a child of FOO.
 
385
        """
378
386
        # XXX: Need to think more here about when the user has
379
387
        # made a specific decision on a particular value -- c.f.
380
388
        # mark-merge.  
381
389
        for path, ie in self.new_inv.iter_entries():
382
 
            old_version = None
383
 
            file_id = ie.file_id
384
 
            for parent_tree in self.parent_trees:
385
 
                parent_inv = parent_tree.inventory
386
 
                if file_id not in parent_inv:
387
 
                    continue
388
 
                parent_ie = parent_inv[file_id]
389
 
                if parent_ie.parent_id != ie.parent_id:
390
 
                    old_version = None
391
 
                    break
392
 
                elif parent_ie.name != ie.name:
393
 
                    old_version = None
394
 
                    break
395
 
                elif old_version is None:
396
 
                    old_version = parent_ie.name_version
397
 
                elif old_version != parent_ie.name_version:
398
 
                    old_version = None
399
 
                    break
400
 
                else:
401
 
                    pass                # so far so good
402
 
            if old_version is None:
403
 
                mutter('new name_version for {%s}', file_id)
404
 
                ie.name_version = self.rev_id
 
390
            previous_entries = self._find_entry_parents(ie. file_id)
 
391
            if ie.revision is None:
 
392
                change = ie.snapshot(self.rev_id, path, previous_entries,
 
393
                                     self.work_tree, self.weave_store)
405
394
            else:
406
 
                mutter('name_version for {%s} inherited as {%s}',
407
 
                       file_id, old_version)
408
 
                ie.name_version = old_version
409
 
 
410
 
 
411
 
    def _store_entries(self):
412
 
        """Build revision inventory and store modified files.
413
 
 
414
 
        This is called with new_inv a new empty inventory.  Depending on
415
 
        which files are selected for commit, and which ones have
416
 
        been modified or merged, new inventory entries are built
417
 
        based on the working and parent inventories.
418
 
 
419
 
        As a side-effect this stores new text versions for committed
420
 
        files with text changes or merges.
421
 
 
422
 
        Each entry can have one of several things happen:
423
 
 
424
 
        carry_file -- carried from the previous version (if not
425
 
            selected for commit)
426
 
 
427
 
        commit_nonfile -- no text to worry about
428
 
 
429
 
        commit_old_text -- same text, may have moved
430
 
 
431
 
        commit_file -- new text version
 
395
                change = "unchanged"
 
396
            note("%s %s", change, path)
 
397
 
 
398
    def _populate_new_inv(self):
 
399
        """Build revision inventory.
 
400
 
 
401
        This creates a new empty inventory. Depending on
 
402
        which files are selected for commit, and what is present in the
 
403
        current tree, the new inventory is populated. inventory entries 
 
404
        which are candidates for modification have their revision set to
 
405
        None; inventory entries that are carried over untouched have their
 
406
        revision set to their prior value.
432
407
        """
 
408
        mutter("Selecting files for commit with filter %s", self.specific_files)
 
409
        self.new_inv = Inventory()
433
410
        for path, new_ie in self.work_inv.iter_entries():
434
411
            file_id = new_ie.file_id
435
412
            mutter('check %s {%s}', path, new_ie.file_id)
438
415
                    mutter('%s not selected for commit', path)
439
416
                    self._carry_file(file_id)
440
417
                    continue
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
 
 
 
418
            mutter('%s selected for commit', path)
 
419
            ie = new_ie.copy()
 
420
            ie.revision = None
 
421
            self.new_inv.add(ie)
468
422
 
469
423
    def _carry_file(self, file_id):
470
424
        """Carry the file unchanged from the basis revision."""
471
425
        if self.basis_inv.has_id(file_id):
472
426
            self.new_inv.add(self.basis_inv[file_id].copy())
473
427
 
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
428
    def _report_deletes(self):
485
429
        for file_id in self.basis_inv:
486
430
            if file_id not in self.new_inv:
487
431
                note('deleted %s', self.basis_inv.id2path(file_id))
488
432
 
489
433
 
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
 
 
500
 
 
501
 
    def _add_text_to_weave(self, file_id, new_lines, parents):
502
 
        self.weave_store.add_text(file_id, self.rev_id, new_lines, parents)
503
 
 
504
434
 
505
435
def _gen_revision_id(branch, when):
506
436
    """Return new revision-id."""