=== modified file 'NEWS' --- old/NEWS 2006-10-11 00:23:23 +0000 +++ new/NEWS 2006-10-18 02:31:11 +0000 @@ -38,7 +38,17 @@ * Leave HttpTransportBase daughter classes decides how they implement cloning. (Vincent Ladeuil, #61606) - * diff3 does not indicate conflicts on clean merge. (Aaron Bentley) + * diff3 does not indicate conflicts on clean merge. (Aaron Bentley) + + * ``bzr ignore`` lists ignores + ``branch-root/$ bzr ignore ./`` inserts + between './' and + ``bzr ignore /absolute/path/branch-root//`` + for patterns containing absolute paths inside branch, creates + branch-root-relative patterns; + ``bzr ignore /absolute/path/`` for patterns containing + absolute paths outside branch, aborts with error. + (Richard Wilbur, #3780) TESTING: === modified file 'bzrlib/builtins.py' --- old/bzrlib/builtins.py 2006-10-11 00:23:23 +0000 +++ new/bzrlib/builtins.py 2006-10-18 02:08:36 +0000 @@ -1533,10 +1533,15 @@ To remove patterns from the ignore list, edit the .bzrignore file. + To see patterns in the ignore list, invoke without any argument. + If the pattern contains a slash, it is compared to the whole path from the branch root. Otherwise, it is compared to only the last - component of the path. To match a file only in the root directory, - prepend './'. + component of the path. To match a file only in the current + directory, prepend './'. Patterns containing absolute paths + inside the branch are processed into root-relative form. A + pattern containing an absolute path outside the branch will cause + ignore to abort. Ignore patterns are case-insensitive on case-insensitive systems. @@ -1546,7 +1551,6 @@ bzr ignore ./Makefile bzr ignore '*.class' """ - # TODO: Complain if the filename is absolute takes_args = ['name_pattern?'] takes_options = [ Option('old-default-rules', @@ -1560,8 +1564,7 @@ for pattern in ignores.OLD_DEFAULTS: print pattern return - if name_pattern is None: - raise errors.BzrCommandError("ignore requires a NAME_PATTERN") + tree, relpath = WorkingTree.open_containing(u'.') ifn = tree.abspath('.bzrignore') if os.path.exists(ifn): @@ -1578,21 +1581,46 @@ if igns and igns[-1] != '\n': igns += '\n' - igns += name_pattern + '\n' - - f = AtomicFile(ifn, 'wt') - try: - f.write(igns.encode('utf-8')) - f.commit() - finally: - f.close() - - inv = tree.inventory - if inv.path2id('.bzrignore'): - mutter('.bzrignore is already versioned') + # If no pattern specified, print filename and dump current list. + if not name_pattern: + print "%s:\n%s" % (ifn, igns) + # Otherwise, pattern specified. else: - mutter('need to make new .bzrignore file versioned') - tree.add(['.bzrignore']) + # If local directory-relative pattern, prefix with + # root-relative path. + if (len(name_pattern) >= 2 and name_pattern[0:2] == './' and + relpath): + name_pattern = './' + relpath + name_pattern[1:] + # If absolute path ... + elif name_pattern[0] == '/': + # is contained in root directory, create root-relative + # path. + if (len(name_pattern) > len(tree.basedir) and + name_pattern[0:len(tree.basedir)] == tree.basedir and + name_pattern[len(tree.basedir)] == '/'): + name_pattern = '.' + name_pattern[len(tree.basedir):] + # is outside root directory, notify user of error. + else: + message = str("ignore: Path\n%s\n" + "\toutside current working tree:\n%s\n" % + (name_pattern, tree.basedir)) + raise errors.BzrCommandError(message) + + igns += name_pattern + '\n' + + f = AtomicFile(ifn, 'wt') + try: + f.write(igns.encode('utf-8')) + f.commit() + finally: + f.close() + + inv = tree.inventory + if inv.path2id('.bzrignore'): + mutter('.bzrignore is already versioned') + else: + mutter('need to make new .bzrignore file versioned') + tree.add(['.bzrignore']) class cmd_ignored(Command): @@ -1948,7 +1976,7 @@ """Run internal test suite. This creates temporary test directories in the working directory, - but not existing data is affected. These directories are deleted + but no existing data is affected. These directories are deleted if the tests pass, or left behind to help in debugging if they fail and --keep-output is specified. === modified file 'bzrlib/tests/blackbox/test_ignore.py' --- old/bzrlib/tests/blackbox/test_ignore.py 2006-10-05 22:42:09 +0000 +++ new/bzrlib/tests/blackbox/test_ignore.py 2006-10-18 01:38:32 +0000 @@ -67,15 +67,79 @@ self.assertEquals(self.capture('unknowns'), 'foo.blah\n') self.runbzr('ignore *.blah') self.assertEquals(self.capture('unknowns'), '') - self.assertEquals('*.blah\n', open('.bzrignore', 'rU').read()) + self.check_file_contents('.bzrignore', '*.blah\n') - # 'ignore' works when then .bzrignore file already exists + # 'ignore' works when the .bzrignore file already exists file('garh', 'wt').write('garh') self.assertEquals(self.capture('unknowns'), 'garh\n') self.runbzr('ignore garh') self.assertEquals(self.capture('unknowns'), '') - self.assertEquals(file('.bzrignore', 'rU').read(), '*.blah\ngarh\n') - + self.check_file_contents('.bzrignore', '*.blah\ngarh\n') + + def test_ignore_no_arguments(self): + """'ignore' prints ignore file path and patterns""" + self.runbzr('init') + root = osutils.getcwd() + self.failIfExists('.bzrignore') + out, err = self.runbzr('ignore') + self.failIfExists('.bzrignore') + self.assertEquals(unicode(out), + root + '/.bzrignore:\n\n') + self.assertEqual('', err) + self.runbzr('ignore spam') + self.failUnlessExists('.bzrignore') + self.runbzr('ignore ./*.eggs') + self.failUnlessExists('.bzrignore') + out, err = self.runbzr('ignore') + self.assertEquals(unicode(out), + root + '/.bzrignore:\n' + \ + open(root + '/.bzrignore', 'rU').read() + '\n') + self.assertEqual('', err) + + def test_ignore_absolutes(self): + """test 'ignore ' + + in branch => relative path -> ignore, else error + """ + self.runbzr('init') + root = osutils.getcwd() + # Outside branch => fail + error_message = 'bzr: ERROR: ignore: Path\n' + root + 'spam' + \ + '\n\toutside current working tree:\n' + \ + root + '\n' + self.run_bzr_error((error_message,), 'ignore', root + 'spam') + self.failIfExists('.bzrignore') + # Inside branch => relative path added to .bzrignore + self.run_bzr('mkdir', 'spam') + self.assertEquals(self.capture('unknowns'), '') + file('spam/eggs', 'wb').write('spam and eggs') + self.assertEquals(self.capture('unknowns'), 'spam/eggs\n') + self.runbzr('ignore ' + root + '/spam/eggs') + self.failUnlessExists('.bzrignore') + self.check_file_contents('.bzrignore', './spam/eggs\n') + self.assertEquals(self.capture('unknowns'), '') + + def test_ignore_local_relative(self): + """test non-root 'ignore ./' + + if cwd == root, ./ -> ignore, + else .// -> ignore + """ + self.runbzr('init') + root = osutils.getcwd() + self.run_bzr('mkdir', 'spam') + self.assertEquals(self.capture('unknowns'), '') + file('spam/eggs', 'wb').write('spam and eggs') + self.assertEquals(self.capture('unknowns'), 'spam/eggs\n') + os.chdir('./spam') + self.assertEquals(os.getcwd(), root + '/spam') + self.runbzr('ignore ./eggs') + os.chdir(root) + self.assertEquals(os.getcwd(), root) + self.failUnlessExists('.bzrignore') + self.check_file_contents('.bzrignore', './spam/eggs\n') + self.assertEquals(self.capture('unknowns'), '') + def test_ignore_old_defaults(self): out, err = self.run_bzr('ignore', '--old-default-rules') self.assertContainsRe(out, 'CVS')