Rev 3603: Add support for -x or --exclude to bzr commit, fixing bug 3117. (Robert Collins) in http://people.ubuntu.com/~robertc/baz2.0/3117
Robert Collins
robertc at robertcollins.net
Mon Aug 4 08:30:32 BST 2008
At http://people.ubuntu.com/~robertc/baz2.0/3117
------------------------------------------------------------
revno: 3603
revision-id: robertc at robertcollins.net-20080804072951-tdue3y4bp9893yx9
parent: pqm at pqm.ubuntu.com-20080804032751-myaykx8azatkvlf8
committer: Robert Collins <robertc at robertcollins.net>
branch nick: 3117
timestamp: Mon 2008-08-04 17:29:51 +1000
message:
Add support for -x or --exclude to bzr commit, fixing bug 3117. (Robert Collins)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/builtins.py builtins.py-20050830033751-fc01482b9ca23183
bzrlib/commit.py commit.py-20050511101309-79ec1a0168e0e825
bzrlib/tests/blackbox/test_commit.py test_commit.py-20060212094538-ae88fc861d969db0
bzrlib/tests/workingtree_implementations/test_commit.py test_commit.py-20060421013633-1610ec2331c8190f
=== modified file 'NEWS'
--- a/NEWS 2008-08-01 04:06:18 +0000
+++ b/NEWS 2008-08-04 07:29:51 +0000
@@ -23,6 +23,9 @@
* ``bzr check`` can now be told which elements at a location it should
check. (Daniel Watkins)
+ * Commit now supports ``--exclude`` (or ``-x``) to exclude some files
+ from the commit. (Robert Collins, #3117)
+
* Give a more specific error when target branch is not reachable.
(James Westby)
@@ -94,15 +97,6 @@
INTERNALS:
- * Make it easier to introduce new WorkingTree formats.
- (Ian Clatworthy)
-
- * The code for exporting trees was refactored not to use the
- deprecated ``InventoryEntry`` methods. (Ian Clatworthy)
-
- * RuleSearchers return () instead of [] now when there are no matches.
- (Ian Clatworthy)
-
* ``bzrlib.branchbuilder.BranchBuilder`` is now much more capable of
putting together a real history without having to create a full
WorkingTree. It is recommended that tests that are not directly
@@ -110,6 +104,19 @@
``BranchBuilder.build_snapshot`` or
``TestCaseWithMemoryTree.make_branch_builder``. (John Arbash Meinel)
+ * ``bzrlib.builtins.internal_tree_files`` broken into two giving a new
+ helper ``safe_relpath_files`` - used by the new ``exclude``
+ parameter to commit. (Robert Collins)
+
+ * Make it easier to introduce new WorkingTree formats.
+ (Ian Clatworthy)
+
+ * The code for exporting trees was refactored not to use the
+ deprecated ``InventoryEntry`` methods. (Ian Clatworthy)
+
+ * RuleSearchers return () instead of [] now when there are no matches.
+ (Ian Clatworthy)
+
bzr 1.6beta3 2008-07-17
-----------------------
=== modified file 'bzrlib/builtins.py'
--- a/bzrlib/builtins.py 2008-07-29 08:40:05 +0000
+++ b/bzrlib/builtins.py 2008-08-04 07:29:51 +0000
@@ -87,13 +87,27 @@
if file_list is None or len(file_list) == 0:
return WorkingTree.open_containing(default_branch)[0], file_list
tree = WorkingTree.open_containing(osutils.realpath(file_list[0]))[0]
+ return tree, safe_relpath_files(tree, file_list)
+
+
+def safe_relpath_files(tree, file_list):
+ """Convert file_list into a list of relpaths in tree.
+
+ :param tree: A tree to operate on.
+ :param file_list: A list of user provided paths or None.
+ :return: A list of relative paths.
+ :raises errors.PathNotChild: When a provided path is in a different tree
+ than tree.
+ """
+ if file_list is None:
+ return None
new_list = []
for filename in file_list:
try:
new_list.append(tree.relpath(osutils.dereference_path(filename)))
except errors.PathNotChild:
raise errors.FileInWrongBranch(tree.branch, filename)
- return tree, new_list
+ return new_list
# TODO: Make sure no commands unconditionally use the working directory as a
@@ -2106,6 +2120,12 @@
committed. If a directory is specified then the directory and everything
within it is committed.
+ When excludes are given, they take precedence over selected files.
+ For example, too commit only changes within foo, but not changes within
+ foo/bar::
+
+ bzr commit foo -x foo/bar
+
If author of the change is not the same person as the committer, you can
specify the author's name using the --author option. The name should be
in the same format as a committer-id, e.g. "John Doe <jdoe at example.com>".
@@ -2141,6 +2161,8 @@
_see_also = ['bugs', 'uncommit']
takes_args = ['selected*']
takes_options = [
+ ListOption('exclude', type=str, short_name='x',
+ help="Do not consider changes made to a given path."),
Option('message', type=unicode,
short_name='m',
help="Description of the new revision."),
@@ -2195,7 +2217,7 @@
def run(self, message=None, file=None, verbose=False, selected_list=None,
unchanged=False, strict=False, local=False, fixes=None,
- author=None, show_diff=False):
+ author=None, show_diff=False, exclude=None):
from bzrlib.errors import (
PointlessCommit,
ConflictsInTree,
@@ -2245,7 +2267,7 @@
raise errors.BzrCommandError(
"please specify either --message or --file")
if file:
- my_message = codecs.open(file, 'rt',
+ my_message = codecs.open(file, 'rt',
bzrlib.user_encoding).read()
if my_message == "":
raise errors.BzrCommandError("empty commit message specified")
@@ -2256,7 +2278,8 @@
specific_files=selected_list,
allow_pointless=unchanged, strict=strict, local=local,
reporter=None, verbose=verbose, revprops=properties,
- author=author)
+ author=author,
+ exclude=safe_relpath_files(tree, exclude))
except PointlessCommit:
# FIXME: This should really happen before the file is read in;
# perhaps prepare the commit; get the message; then actually commit
=== modified file 'bzrlib/commit.py'
--- a/bzrlib/commit.py 2008-04-24 07:22:53 +0000
+++ b/bzrlib/commit.py 2008-08-04 07:29:51 +0000
@@ -204,7 +204,8 @@
reporter=None,
config=None,
message_callback=None,
- recursive='down'):
+ recursive='down',
+ exclude=None):
"""Commit working copy as a new revision.
:param message: the commit message (it or message_callback is required)
@@ -232,6 +233,9 @@
:param verbose: if True and the reporter is not None, report everything
:param recursive: If set to 'down', commit in any subtrees that have
pending changes of any sort during this commit.
+ :param exclude: None or a list of relative paths to exclude from the
+ commit. Pending changes to excluded files will be ignored by the
+ commit.
"""
mutter('preparing to commit')
@@ -255,6 +259,11 @@
self.bound_branch = None
self.any_entries_changed = False
self.any_entries_deleted = False
+ if exclude is not None:
+ self.exclude = sorted(
+ minimum_path_selection(exclude))
+ else:
+ self.exclude = []
self.local = local
self.master_branch = None
self.master_locked = False
@@ -329,12 +338,15 @@
self.pb.show_count = True
self.pb.show_bar = True
+ self.basis_inv = self.basis_tree.inventory
+ self._gather_parents()
# After a merge, a selected file commit is not supported.
# See 'bzr help merge' for an explanation as to why.
- self.basis_inv = self.basis_tree.inventory
- self._gather_parents()
if len(self.parents) > 1 and self.specific_files:
raise errors.CannotCommitSelectedFileMerge(self.specific_files)
+ # Excludes are a form of selected file commit.
+ if len(self.parents) > 1 and self.exclude:
+ raise errors.CannotCommitSelectedFileMerge(self.exclude)
# Collect the changes
self._set_progress_stage("Collecting changes",
@@ -648,28 +660,30 @@
# in bugs like #46635. Any reason not to use/enhance Tree.changes_from?
# ADHB 11-07-2006
+ exclude = self.exclude
specific_files = self.specific_files
mutter("Selecting files for commit with filter %s", specific_files)
# Build the new inventory
- self._populate_from_inventory(specific_files)
+ self._populate_from_inventory()
# If specific files are selected, then all un-selected files must be
# recorded in their previous state. For more details, see
# https://lists.ubuntu.com/archives/bazaar/2007q3/028476.html.
- if specific_files:
+ if specific_files or exclude:
for path, old_ie in self.basis_inv.iter_entries():
if old_ie.file_id in self.builder.new_inventory:
# already added - skip.
continue
- if is_inside_any(specific_files, path):
- # was inside the selected path, if not present it has been
- # deleted so skip.
+ if (is_inside_any(specific_files, path)
+ and not is_inside_any(exclude, path)):
+ # was inside the selected path, and not excluded - if not
+ # present it has been deleted so skip.
continue
+ # From here down it was either not selected, or was excluded:
if old_ie.kind == 'directory':
self._next_progress_entry()
- # not in final inv yet, was not in the selected files, so is an
- # entry to be preserved unaltered.
+ # We preserve the entry unaltered.
ie = old_ie.copy()
# Note: specific file commits after a merge are currently
# prohibited. This test is for sanity/safety in case it's
@@ -697,13 +711,15 @@
self._basis_delta.append((path, None, file_id, None))
self.reporter.deleted(path)
- def _populate_from_inventory(self, specific_files):
+ def _populate_from_inventory(self):
"""Populate the CommitBuilder by walking the working tree inventory."""
if self.strict:
# raise an exception as soon as we find a single unknown.
for unknown in self.work_tree.unknowns():
raise StrictCommitFailed()
-
+
+ specific_files = self.specific_files
+ exclude = self.exclude
report_changes = self.reporter.is_verbose()
deleted_ids = []
# A tree of paths that have been deleted. E.g. if foo/bar has been
@@ -712,6 +728,8 @@
# XXX: Note that entries may have the wrong kind because the entry does
# not reflect the status on disk.
work_inv = self.work_tree.inventory
+ # NB: entries will include entries within the excluded ids/paths
+ # because iter_entries_by_dir has no 'exclude' facility today.
entries = work_inv.iter_entries_by_dir(
specific_file_ids=self.specific_file_ids, yield_parents=True)
for path, existing_ie in entries:
@@ -739,6 +757,10 @@
if deleted_dict is not None:
# the path has a deleted parent, do not add it.
continue
+ if exclude and is_inside_any(exclude, path):
+ # Skip - it is to be considered by the final copy-from-basis
+ # step.
+ continue
content_summary = self.work_tree.path_content_summary(path)
# Note that when a filter of specific files is given, we must only
# skip/record deleted files matching that filter.
=== modified file 'bzrlib/tests/blackbox/test_commit.py'
--- a/bzrlib/tests/blackbox/test_commit.py 2007-12-10 16:39:00 +0000
+++ b/bzrlib/tests/blackbox/test_commit.py 2008-08-04 07:29:51 +0000
@@ -331,6 +331,34 @@
self.build_tree_contents([('u1/hosts', 'merge resolution\n')])
self.run_bzr('commit -m checkin-merge-of-the-offline-work-from-u1 u1')
+ def test_commit_exclude_excludes_modified_files(self):
+ """Commit -x foo should ignore changes to foo."""
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['a', 'b', 'c'])
+ tree.smart_add(['.'])
+ out, err = self.run_bzr(['commit', '-m', 'test', '-x', 'b'])
+ self.assertFalse('added b' in out)
+ self.assertFalse('added b' in err)
+ # If b was ignored it will still be 'added' in status.
+ out, err = self.run_bzr(['added'])
+ self.assertEqual('b\n', out)
+ self.assertEqual('', err)
+
+ def test_commit_exclude_twice_uses_both_rules(self):
+ """Commit -x foo -x bar should ignore changes to foo and bar."""
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['a', 'b', 'c'])
+ tree.smart_add(['.'])
+ out, err = self.run_bzr(['commit', '-m', 'test', '-x', 'b', '-x', 'c'])
+ self.assertFalse('added b' in out)
+ self.assertFalse('added c' in out)
+ self.assertFalse('added b' in err)
+ self.assertFalse('added c' in err)
+ # If b was ignored it will still be 'added' in status.
+ out, err = self.run_bzr(['added'])
+ self.assertEqual('b\nc\n', out)
+ self.assertEqual('', err)
+
def test_commit_respects_spec_for_removals(self):
"""Commit with a file spec should only commit removals that match"""
t = self.make_branch_and_tree('.')
=== modified file 'bzrlib/tests/workingtree_implementations/test_commit.py'
--- a/bzrlib/tests/workingtree_implementations/test_commit.py 2008-04-20 08:21:39 +0000
+++ b/bzrlib/tests/workingtree_implementations/test_commit.py 2008-08-04 07:29:51 +0000
@@ -195,6 +195,31 @@
('xyz/m', 'm-id'),
], paths)
+ def test_commit_exclude_pending_merge_fails(self):
+ """Excludes are a form of partial commit."""
+ wt = self.make_branch_and_tree('.')
+ self.build_tree(['foo'])
+ wt.add('foo')
+ wt.commit('commit one')
+ wt2 = wt.bzrdir.sprout('to').open_workingtree()
+ wt2.commit('change_right')
+ wt.merge_from_branch(wt2.branch)
+ self.assertRaises(errors.CannotCommitSelectedFileMerge,
+ wt.commit, 'test', exclude=['foo'])
+
+ def test_commit_exclude_excludes_modified_files(self):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['a', 'b', 'c'])
+ tree.smart_add(['.'])
+ tree.commit('test', exclude=['b', 'c'])
+ # If b was ignored it will still be 'added' in status.
+ tree.lock_read()
+ self.addCleanup(tree.unlock)
+ changes = list(tree.iter_changes(tree.basis_tree()))
+ self.assertEqual(2, len(changes))
+ self.assertEqual((None, 'b'), changes[0][1])
+ self.assertEqual((None, 'c'), changes[1][1])
+
def test_commit_sets_last_revision(self):
tree = self.make_branch_and_tree('tree')
committed_id = tree.commit('foo', rev_id='foo')
More information about the bazaar-commits
mailing list