[MERGE] Refactor commit to prepare for population by tree walking

Aaron Bentley aaron.bentley at utoronto.ca
Fri Jun 29 21:42:37 BST 2007


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ian Clatworthy wrote:
> === modified file bzrlib/commit.py
> --- bzrlib/commit.py
> +++ bzrlib/commit.py
> @@ -65,7 +65,8 @@
>  import bzrlib.config
>  from bzrlib.errors import (BzrError, PointlessCommit,
>                             ConflictsInTree,
> -                           StrictCommitFailed
> +                           StrictCommitFailed,
> +                           NoSuchId
>                             )

The preferred style is to simply import errors, and then use
errors.NoSuchId.  If you do import NoSuchId, try to maintain
alphabetical order.  And make sure every entry has a comma after it, so
that adding new entries doesn't entail editing the preceeding line.

> -            self.pb.show_bar = False
> +            self.pb.show_bar = True

Thanks :-)

> -        deleted_ids = []
> -        deleted_paths = set()
> -        for path, new_ie in entries:
> -            self._emit_progress_next_entry()
> -            file_id = new_ie.file_id

Hmm.  We really need a diff that doesn't get confused by indenting changes.

> +            root_added_already = True
> +
> +        # Build the new inventory. _populate_from_tree() is the preferred
> +        # direction here but it doesn't support multiple parents yet,
> +        # it triggers a unicode normalisation issue in the test suite and
> +        # it needs more testing.



> +        populator = self._populate_from_inventory
> +        #if len(self.parents) == 1:
> +        #    populator = self._populate_from_tree
> +        populator(specific_files, root_added_already)

^^ this is sort of ugly.  It would be much nicer to restore
populate_from_tree first.

> +            try:
> +                kind = self.work_tree.kind(file_id)
> +                # TODO: specific_files filtering before nested tree processing?
> +                if kind == 'tree-reference' and self.recursive == 'down':
> +                    self._commit_nested_tree(path)

^^^ Yeah.  No doubt a thinko on my part.
> +            self._record_entry(path, file_id, specific_files, kind, name,
> +                parent_id, definitely_changed, new_ie)
> +
> +        # Unversion IDs that were found to be deleted
> +        self.work_tree.unversion(deleted_ids)

^^^ Doing this last disturbs me.  You can't normally do unversioning
last, because you may need to put something else into the same position.
 This one is okay, I guess.

> +    def _populate_from_tree(self, specific_files, root_added_already):
> +        """Populate the CommitBuilder by walking the working tree."""
> +        # Until trees supports an "iter_commitable" iterator that
> +        # understand multiple parents, this assertion is required.
> +        assert len(self.parents) == 1
> +
> +        deleted_ids = []
> +        deleted_paths = set()
> +        entries = self.work_tree._iter_changes(self.basis_tree,
> +            include_unchanged=True, want_unversioned=True,
> +            require_versioned=False)

I don't see why you've got require_versioned=False.  That only affects
behavior when specific_files is specified.

> +            # Skip unknowns unless strict mode
> +            if versioned_flags == (False,False):
> +                if self.strict:
> +                    raise StrictCommitFailed()
> +                else:
> +                    continue

I don't think you know that the files are unknown at this point.  They
may be ignored.

> +            # TODO: specific_files filtering before nested tree processing?
> +            # TODO: keep track of nested trees and don't recurse into them?
> +            if kind == 'tree-reference' and self.recursive == 'down':
> +                self._commit_nested_tree(path)

^^^ This should include file_id at least.

> +        for id in deleted_ids:
> +            try:
> +                self.work_tree.unversion([id])
> +            except NoSuchId:
> +                pass

^^^ I think it would be better to handle this as another call to
record_entry.

> +    def _commit_nested_tree(self, path):
> +        "Commit a nested tree."
> +        sub_tree = WorkingTree.open(self.work_tree.abspath(path))

^^^ the recommended way of doing this is self.get_nested_tree(self,
file_id, path)

> +        # FIXME: be more comprehensive here:
> +        # this works when both trees are in --trees repository,
> +        # but when both are bound to a different repository,
> +        # it fails;

So the concern here is about the case where they are using the same
repository as bound branches?

> +    def _record_entry(self, path, file_id, specific_files, kind, name,
> +                      parent_id, definitely_changed, existing_ie=None):

^^^ I think this should be building up an inventory delta.  That's meant
to be the new way of manipulating tree contents.  Basically, we
shouldn't be touching Tree.inventory at all.

> +        "Record the new inventory entry for a path if any."
> +        # mutter('check %s {%s}', path, file_id)
> +        if (not specific_files or 
> +            is_inside_or_parent_of_any(specific_files, path)):
> +                # mutter('%s selected for commit', path)
> +                if definitely_changed or existing_ie is None:
> +                    ie = inventory.make_entry(kind, name, parent_id, file_id)
> +                else:
> +                    ie = existing_ie.copy()
> +                    ie.revision = None
> +        else:
> +            # mutter('%s not selected for commit', path)
> +            if self.basis_inv.has_id(file_id):

^^^ I think there's a tree method, but anyway, this is data you already
know from iter_changes.

> +    def _report_change(self, ie, path):
> +        """Report a change to the user.
> +
> +        The change that has occurred is described relative to the basis
> +        inventory.
> +        """
> +        if (self.basis_inv.has_id(ie.file_id)):
> +            basis_ie = self.basis_inv[ie.file_id]
> +        else:
> +            basis_ie = None
> +        change = ie.describe_change(basis_ie, ie)
> +        if change in (InventoryEntry.RENAMED, 
> +            InventoryEntry.MODIFIED_AND_RENAMED):
> +            old_path = self.basis_inv.id2path(ie.file_id)
> +            self.reporter.renamed(change, old_path, path)
> +        else:
> +            self.reporter.snapshot_change(change, path)

I think it would be nice to reuse delta._ChangeReporter here.
Consistency FTW.  Also not using inventories FTW.

> +    def _emit_progress_set_stage(self, name, entries_title=None):
>          """Set the progress stage and emit an update to the progress bar."""

^^^ does this need to be emitting progress *and* setting stage?

Aaron
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD4DBQFGhW690F+nu1YWqI0RAjo0AJ935IeK63Cj7Xw4VfcT/fMJWK4nZACYwcgL
Bzrscux/f7PrNDbzHTlFig==
=XCXE
-----END PGP SIGNATURE-----



More information about the bazaar mailing list