Merge lp:~bialix/bzr/clean-tree-bzrdir into lp:bzr

Proposed by Alexander Belchenko
Status: Merged
Approved by: Vincent Ladeuil
Approved revision: no longer in the source branch.
Merged at revision: 5212
Proposed branch: lp:~bialix/bzr/clean-tree-bzrdir
Merge into: lp:bzr
Diff against target: 100 lines (+45/-0)
3 files modified
NEWS (+4/-0)
bzrlib/clean_tree.py (+25/-0)
bzrlib/tests/blackbox/test_clean_tree.py (+16/-0)
To merge this branch: bzr merge lp:~bialix/bzr/clean-tree-bzrdir
Reviewer Review Type Date Requested Status
Parth Malwankar Approve
Vincent Ladeuil Approve
Aaron Bentley Pending
Review via email: mp+24479@code.launchpad.net

Commit message

``bzr clean-tree`` should not delete nested bzrdirs. (bialix, #572098)

Description of the change

This is minimal patch to address the problem highlighted in the bug report https://bugs.launchpad.net/bzr/+bug/572098. I've chatted with Aaron some time ago and he has agreed clean-tree should not delete nested bzrdirs by default.

I'd like to get this patch backported to 2.1 series as well, because it's utterly important for users of bzr-externals and scmproj.

Next my step will be to provide additional command-line flag to force deleting of bzrdirs if user wants it.

To post a comment you must log in.
Revision history for this message
Parth Malwankar (parthm) wrote :

I was wondering if it makes sense to issue an info message in verbose mode saying something like '"foo" branch is skipped. "bar" branch is skipped... Please consider adding to ignored list.' rather than silently skipping.

run_bzr calls can probably be updated to take a list instead of a string. IIUC thats the newer recommended way.

Revision history for this message
Alexander Belchenko (bialix) wrote :

Parth Malwankar пишет:
> I was wondering if it makes sense to issue an info message in verbose mode saying something like '"foo" branch is skipped. "bar" branch is skipped... Please consider adding to ignored list.' rather than silently skipping.

"Please adding to ignore list"? I don't think so.
In my case they're already added to ignore list, and my intent is to run `bzr clean-tree --ignored`
without worrying about nested branches.

I'm a little ambivalent on wording though: nested bzrdir could be repository, checkout, lightweight
checkout, and not only a branch. So if I'll say '"foo" branch is skipped' while it's not branch,
then I will lie to user. I don't want to make bzr liar. I've asked Martin Pool is there any English
word for calling any bzr object (branch/repo/checkout/light checkout) without specifying its
existing type. I'm leaning towards "bzr object" but I'm not sure it's right. What about svn working
copy which could be handled by bzr-svn?

> run_bzr calls can probably be updated to take a list instead of a string. IIUC thats the newer recommended way.

I've used the same style as of other tests in that file. I don't mind to change it if this is main
blocker. Should I update existing tests in that file as well?

--
All the dude wanted was his rug back

Revision history for this message
Parth Malwankar (parthm) wrote :

> Parth Malwankar пишет:
> > I was wondering if it makes sense to issue an info message in verbose mode
> saying something like '"foo" branch is skipped. "bar" branch is skipped...
> Please consider adding to ignored list.' rather than silently skipping.
>
> "Please adding to ignore list"? I don't think so.
> In my case they're already added to ignore list, and my intent is to run `bzr
> clean-tree --ignored`
> without worrying about nested branches.
>

Makes sense.

> I'm a little ambivalent on wording though: nested bzrdir could be repository,
> checkout, lightweight
> checkout, and not only a branch. So if I'll say '"foo" branch is skipped'
> while it's not branch,
> then I will lie to user. I don't want to make bzr liar. I've asked Martin Pool
> is there any English
> word for calling any bzr object (branch/repo/checkout/light checkout) without
> specifying its
> existing type. I'm leaning towards "bzr object" but I'm not sure it's right.
> What about svn working
> copy which could be handled by bzr-svn?
>

I don't really have a good suggestion for the message.
Maybe something like 'skipped separately versioned directory "foo"'?

> > run_bzr calls can probably be updated to take a list instead of a string.
> IIUC thats the newer recommended way.
>
> I've used the same style as of other tests in that file. I don't mind to
> change it if this is main
> blocker. Should I update existing tests in that file as well?
>

I don't think its a blocker. I haven't really been touching
existing test, only following this for new ones.

Revision history for this message
Vincent Ladeuil (vila) wrote :

I think there is potentially a huge performance impact on big trees here.

For all *files* you check that the *directory* is not a branch.

The correct way to do this would be to prune the directory and *all* of its content when it's first processed.

Since that requires changing tree.extras() and would widen the scope of this proposal significantly,
I'm not asking you to take this into account, but we need a big flashing red comment in filter_out_nested_bzrdirs to mention it.

And since fixing it will make this function obsolete, please make it private by adding a leading '_'
so we don't have to enter into the deprecation dance for it when it occurs.

Consider this proposal approved if you agree with this tweak.

Some nits:
73 + # bug https://bugs.launchpad.net/bzr/+bug/572098

A couple of words to explain the bug would help to understand the test when offline :)

78 + self.run_bzr('clean-tree --unknown --force')
79 + self.failUnlessExists('foo')
80 + self.failUnlessExists('bar')
81 + self.run_bzr('clean-tree --ignored --force')
82 + self.failUnlessExists('foo')
83 + self.failUnlessExists('bar')

You may want to make two different tests here and factor out the lines to prepare the working tree
(a dedicated class sounds overkill).

review: Needs Fixing
Revision history for this message
Alexander Belchenko (bialix) wrote :

Updated version of the patch is available, please review.

Revision history for this message
Vincent Ladeuil (vila) wrote :

The 'commit message' in the mp is the one used by pqm, please make it one-line long instead
(it's actual content is really your COVER letter here).

Regarding the FIXME you added, @display_command is the way to add outf support, but here, you may want to
use ui.ui_factory_show_message() instead.

Revision history for this message
Vincent Ladeuil (vila) :
review: Approve
Revision history for this message
Alexander Belchenko (bialix) wrote :

Vincent Ladeuil пишет:
> The 'commit message' in the mp is the one used by pqm, please make it one-line long instead
> (it's actual content is really your COVER letter here).

I don't understand you.

> Regarding the FIXME you added, @display_command is the way to add outf support, but here, you may want to
> use ui.ui_factory_show_message() instead.

I'm not sure what I want here, but anyway it will require API change,
and I'd like to backport this patch to 2.1 first before introducing more
invasive changes.

--
All the dude wanted was his rug back

Revision history for this message
Parth Malwankar (parthm) wrote :

Looks good to me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-05-03 04:08:50 +0000
3+++ NEWS 2010-05-04 10:32:59 +0000
4@@ -38,6 +38,10 @@
5 better with sudo.
6 (Martin <gzlist@googlemail.com>, Parth Malwankar, #376388)
7
8+* ``bzr clean-tree`` should not delete nested bzrdirs. Required for proper
9+ support of bzr-externals and scmproj plugins.
10+ (Alexander Belchenko, bug #572098)
11+
12 * ``bzr log --exclude-common-ancestry -r X..Y`` displays the revisions that
13 are part of Y ancestry but not part of X ancestry (aka the graph
14 difference).
15
16=== modified file 'bzrlib/clean_tree.py'
17--- bzrlib/clean_tree.py 2010-04-30 11:03:59 +0000
18+++ bzrlib/clean_tree.py 2010-05-04 10:32:59 +0000
19@@ -18,6 +18,7 @@
20 import os
21 import shutil
22
23+from bzrlib import bzrdir, errors
24 from bzrlib.osutils import isdir
25 from bzrlib.trace import note
26 from bzrlib.workingtree import WorkingTree
27@@ -51,11 +52,14 @@
28 try:
29 deletables = list(iter_deletables(tree, unknown=unknown,
30 ignored=ignored, detritus=detritus))
31+ deletables = _filter_out_nested_bzrdirs(deletables)
32 if len(deletables) == 0:
33 note('Nothing to delete.')
34 return 0
35 if not no_prompt:
36 for path, subp in deletables:
37+ # FIXME using print is very bad idea
38+ # clean_tree should accept to_file argument to write the output
39 print subp
40 val = raw_input('Are you sure you wish to delete these [y/N]?')
41 if val.lower() not in ('y', 'yes'):
42@@ -66,6 +70,27 @@
43 tree.unlock()
44
45
46+def _filter_out_nested_bzrdirs(deletables):
47+ result = []
48+ for path, subp in deletables:
49+ # bzr won't recurse into unknowns/ignored directories by default
50+ # so we don't pay a penalty for checking subdirs of path for nested
51+ # bzrdir.
52+ # That said we won't detect the branch in the subdir of non-branch
53+ # directory and therefore delete it. (worth to FIXME?)
54+ if isdir(path):
55+ try:
56+ bzrdir.BzrDir.open(path)
57+ except errors.NotBranchError:
58+ result.append((path,subp))
59+ else:
60+ # TODO may be we need to notify user about skipped directories?
61+ pass
62+ else:
63+ result.append((path,subp))
64+ return result
65+
66+
67 def delete_items(deletables, dry_run=False):
68 """Delete files in the deletables iterable"""
69 has_deleted = False
70
71=== modified file 'bzrlib/tests/blackbox/test_clean_tree.py'
72--- bzrlib/tests/blackbox/test_clean_tree.py 2009-03-23 14:59:43 +0000
73+++ bzrlib/tests/blackbox/test_clean_tree.py 2010-05-04 10:32:59 +0000
74@@ -21,6 +21,7 @@
75
76 import os
77
78+from bzrlib import ignores
79 from bzrlib.tests import TestCaseWithTransport
80
81
82@@ -62,3 +63,18 @@
83 self.failIfExists('name')
84 self.failIfExists('name~')
85 self.failIfExists('name.pyc')
86+
87+ def test_clean_tree_nested_bzrdir(self):
88+ # clean-tree should not blindly delete nested bzrdirs (branches)
89+ # bug https://bugs.launchpad.net/bzr/+bug/572098
90+ # so it will play well with scmproj/bzr-externals plugins.
91+ wt1 = self.make_branch_and_tree('.')
92+ wt2 = self.make_branch_and_tree('foo')
93+ wt3 = self.make_branch_and_tree('bar')
94+ ignores.tree_ignores_add_patterns(wt1, ['./foo'])
95+ self.run_bzr(['clean-tree', '--unknown', '--force'])
96+ self.failUnlessExists('foo')
97+ self.failUnlessExists('bar')
98+ self.run_bzr(['clean-tree', '--ignored', '--force'])
99+ self.failUnlessExists('foo')
100+ self.failUnlessExists('bar')