Rev 3603: (robertc) Implement a --exclude option for commit which allows the in file:///home/pqm/archives/thelove/bzr/%2Btrunk/
Canonical.com Patch Queue Manager
pqm at pqm.ubuntu.com
Tue Aug 5 02:14:12 BST 2008
At file:///home/pqm/archives/thelove/bzr/%2Btrunk/
------------------------------------------------------------
revno: 3603
revision-id: pqm at pqm.ubuntu.com-20080805011407-wmq7130znc0e6c4x
parent: pqm at pqm.ubuntu.com-20080804032751-myaykx8azatkvlf8
parent: robertc at robertcollins.net-20080805004328-ilb5p3l9lbmix8oe
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Tue 2008-08-05 02:14:07 +0100
message:
(robertc) Implement a --exclude option for commit which allows the
exclusion of paths from the commit change detection logic.
(Robert Collins, \#3117)
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/__init__.py __init__.py-20050309040759-33e65acf91bbcd5d
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/test_options.py testoptions.py-20051014093702-96457cfc86319a8f
bzrlib/tests/workingtree_implementations/test_commit.py test_commit.py-20060421013633-1610ec2331c8190f
------------------------------------------------------------
revno: 3602.1.5
revision-id: robertc at robertcollins.net-20080805004328-ilb5p3l9lbmix8oe
parent: robertc at robertcollins.net-20080805000619-bfje55v540epfkvr
committer: Robert Collins <robertc at robertcollins.net>
branch nick: integration
timestamp: Tue 2008-08-05 10:43:28 +1000
message:
Fixups for test_options due to the changes to cmd_commit - insta-review by spiv.
modified:
bzrlib/tests/test_options.py testoptions.py-20051014093702-96457cfc86319a8f
------------------------------------------------------------
revno: 3602.1.4
revision-id: robertc at robertcollins.net-20080805000619-bfje55v540epfkvr
parent: robertc at robertcollins.net-20080804224118-pgxd6gg6a54luvrv
committer: Robert Collins <robertc at robertcollins.net>
branch nick: 3117
timestamp: Tue 2008-08-05 10:06:19 +1000
message:
Andrew's review feedback.
modified:
bzrlib/tests/blackbox/test_commit.py test_commit.py-20060212094538-ae88fc861d969db0
bzrlib/tests/workingtree_implementations/test_commit.py test_commit.py-20060421013633-1610ec2331c8190f
------------------------------------------------------------
revno: 3602.1.3
revision-id: robertc at robertcollins.net-20080804224118-pgxd6gg6a54luvrv
parent: robertc at robertcollins.net-20080804220734-xoo8j0kt3bdy26s9
committer: Robert Collins <robertc at robertcollins.net>
branch nick: 3117
timestamp: Tue 2008-08-05 08:41:18 +1000
message:
Document the change in the command lines use of MutablelTree and bump the API minimum version.
modified:
NEWS NEWS-20050323055033-4e00b5db738777ff
bzrlib/__init__.py __init__.py-20050309040759-33e65acf91bbcd5d
------------------------------------------------------------
revno: 3602.1.2
revision-id: robertc at robertcollins.net-20080804220734-xoo8j0kt3bdy26s9
parent: robertc at robertcollins.net-20080804072951-tdue3y4bp9893yx9
committer: Robert Collins <robertc at robertcollins.net>
branch nick: 3117
timestamp: Tue 2008-08-05 08:07:34 +1000
message:
Review feedback : test for PointlessCommit and that the example given in the help (excluding a subtree of a specified tree) does in fact work.
modified:
bzrlib/commit.py commit.py-20050511101309-79ec1a0168e0e825
bzrlib/tests/workingtree_implementations/test_commit.py test_commit.py-20060421013633-1610ec2331c8190f
------------------------------------------------------------
revno: 3602.1.1
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 22:41:18 +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)
@@ -73,6 +76,15 @@
API CHANGES:
+ * ``MutableTree.commit`` has an extra optional keywork parameter
+ ``exclude`` that will be unconditionally supplied by the command
+ line UI - plugins that add tree formats may need an update.
+ (Robert Collins)
+
+ * The API minimum version for plugin compatibility has been raised to
+ 1.6 - there are significant changes throughout the code base.
+ (Robert Collins)
+
* The generic fetch code now uses three attributes on Repository objects
to control fetch. The streams requested are controlled via :
``_fetch_order`` and ``_fetch_uses_deltas``. Setting these
@@ -94,15 +106,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 +113,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/__init__.py'
--- a/bzrlib/__init__.py 2008-07-17 19:11:37 +0000
+++ b/bzrlib/__init__.py 2008-08-04 22:41:18 +0000
@@ -44,8 +44,8 @@
version_info = (1, 6, 0, 'beta', 4)
-# API compatibility version: bzrlib is currently API compatible with 0.18.
-api_minimum_version = (0, 18, 0)
+# API compatibility version: bzrlib is currently API compatible with 1.6.
+api_minimum_version = (1, 6, 0)
def _format_version_tuple(version_info):
"""Turn a version number 3-tuple or 5-tuple into a short string.
=== 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 22:07:34 +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
- specific_files = self.specific_files
+ exclude = self.exclude
+ specific_files = self.specific_files or []
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-05 00:06:19 +0000
@@ -331,6 +331,35 @@
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 excluded 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 excluded it will still be 'added' in status.
+ out, err = self.run_bzr(['added'])
+ self.assertTrue('b\n' in out)
+ self.assertTrue('c\n' in 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/test_options.py'
--- a/bzrlib/tests/test_options.py 2007-09-03 01:50:29 +0000
+++ b/bzrlib/tests/test_options.py 2008-08-05 00:43:28 +0000
@@ -17,15 +17,12 @@
import re
from bzrlib import (
- builtins,
bzrdir,
commands,
errors,
option,
- repository,
- symbol_versioning,
)
-from bzrlib.builtins import cmd_commit, cmd_log, cmd_status
+from bzrlib.builtins import cmd_commit
from bzrlib.commands import Command, parse_args
from bzrlib.tests import TestCase
from bzrlib.repofmt import knitrepo
@@ -41,16 +38,18 @@
def test_parse_args(self):
"""Option parser"""
- eq = self.assertEquals
- eq(parse_args(cmd_commit(), ['--help']),
- ([], {'fixes': [], 'help': True}))
- eq(parse_args(cmd_commit(), ['--message=biter']),
- ([], {'fixes': [], 'message': 'biter'}))
+ # XXX: Using cmd_commit makes these tests overly sensitive to changes
+ # to cmd_commit, when they are meant to be about option parsing in
+ # general.
+ self.assertEqual(parse_args(cmd_commit(), ['--help']),
+ ([], {'exclude': [], 'fixes': [], 'help': True}))
+ self.assertEqual(parse_args(cmd_commit(), ['--message=biter']),
+ ([], {'exclude': [], 'fixes': [], 'message': 'biter'}))
def test_no_more_opts(self):
"""Terminated options"""
- self.assertEquals(parse_args(cmd_commit(), ['--', '-file-with-dashes']),
- (['-file-with-dashes'], {'fixes': []}))
+ self.assertEqual(parse_args(cmd_commit(), ['--', '-file-with-dashes']),
+ (['-file-with-dashes'], {'exclude': [], 'fixes': []}))
def test_option_help(self):
"""Options have help strings."""
@@ -67,7 +66,7 @@
def test_option_arg_help(self):
"""Help message shows option arguments."""
out, err = self.run_bzr('help commit')
- self.assertEquals(err, '')
+ self.assertEqual(err, '')
self.assertContainsRe(out, r'--file[ =]MSGFILE')
def test_unknown_short_opt(self):
@@ -81,8 +80,7 @@
def test_allow_dash(self):
"""Test that we can pass a plain '-' as an argument."""
- self.assertEqual(
- (['-'], {'fixes': []}), parse_args(cmd_commit(), ['-']))
+ self.assertEqual((['-']), parse_args(cmd_commit(), ['-'])[0])
def parse(self, options, args):
parser = option.get_optparser(dict((o.name, o) for o in options))
=== 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-05 00:06:19 +0000
@@ -195,6 +195,52 @@
('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_exclude_changed_is_pointless(self):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['a'])
+ tree.smart_add(['.'])
+ tree.commit('setup test')
+ self.build_tree_contents([('a', 'new contents for "a"\n')])
+ self.assertRaises(errors.PointlessCommit, tree.commit, 'test',
+ exclude=['a'], allow_pointless=False)
+
+ 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 excluded 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_exclude_subtree_of_selected(self):
+ tree = self.make_branch_and_tree('.')
+ self.build_tree(['a/', 'a/b'])
+ tree.smart_add(['.'])
+ tree.commit('test', exclude=['a/b'])
+ # If a/b was excluded it will still be 'added' in status.
+ tree.lock_read()
+ self.addCleanup(tree.unlock)
+ changes = list(tree.iter_changes(tree.basis_tree()))
+ self.assertEqual(1, len(changes))
+ self.assertEqual((None, 'a/b'), changes[0][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