Merge lp:~jameinel/bzr/2.0.1-322807-branch-at-root into lp:bzr/2.0

Proposed by John A Meinel
Status: Merged
Approved by: Andrew Bennetts
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~jameinel/bzr/2.0.1-322807-branch-at-root
Merge into: lp:bzr/2.0
Diff against target: 154 lines
3 files modified
NEWS (+20/-21)
bzrlib/osutils.py (+9/-2)
bzrlib/tests/test_osutils.py (+43/-0)
To merge this branch: bzr merge lp:~jameinel/bzr/2.0.1-322807-branch-at-root
Reviewer Review Type Date Requested Status
Andrew Bennetts Needs Fixing
Review via email: mp+12936@code.launchpad.net
To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

This fixes bug #322807

Basically, cicp_canonical_relpath always assumed that 'abspath()' returned a path that did not have a trailing '/'. However, when working at the root of the filesytem, this was not true. (And, I believe, not true on Linux or Mac, either.)

So I changed:
  return current[len(abspath+1):]
to
  return current[len(abspath):].lstrip('/')

which seems to work just fine.

In the process of doing this, I also made the function handle when base doesn't actually exist, since it made testing easier. And I changed it to always run tests that I'm sure will pass. (We don't require the filesystem to have CICP to run _cicp_canonical_relpath, only if we wanted to call it by 'osutils.canonical_relpath()'. This should help increase PQM test coverage.)

Revision history for this message
Andrew Bennetts (spiv) wrote :

This seems good. I don't have a convenient Windows install to test it on, but I assume you've already taken care of that :)

I have some minor suggestions, so I'm voting Needs Fixing and marking the overall review as Approved (as that seems to be the closest approximation of bb:tweak).

81 + try:
82 + next_entries = _listdir(current)
83 + except OSError: # enoent

That's a bit odd. Why not actually check that the exc.errno == ENOENT?

87 for look in _listdir(current):

This repeats the listdir we just did. Why not reuse 'next_entries'?

129 + # This path probably does not exist
130 + # We can't test this on Windows, because it has a 'MIN_ABS_PATHLENGTH'
131 + # check...
132 + # self.assertRelpath('foo', '/', '/foo')

Ideally this would be in a separate test method that could do a requireFeature check, I think. Not a big deal.

review: Needs Fixing
Revision history for this message
John A Meinel (jameinel) wrote :

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

Andrew Bennetts wrote:
> Review: Needs Fixing
> This seems good. I don't have a convenient Windows install to test it on, but I assume you've already taken care of that :)
>
> I have some minor suggestions, so I'm voting Needs Fixing and marking the overall review as Approved (as that seems to be the closest approximation of bb:tweak).
>
> 81 + try:
> 82 + next_entries = _listdir(current)
> 83 + except OSError: # enoent
>
> That's a bit odd. Why not actually check that the exc.errno == ENOENT?

Because I figured EPERM and EANYTHINGELSE would be just as reasonable.
All we are doing here is trying to translate "foo" => "Foo" based on
what is in the filesytem. If we can't access the filesystem, we have
nothing to translate.

>
> 87 for look in _listdir(current):
>
> This repeats the listdir we just did. Why not reuse 'next_entries'?

I'm sure I meant to, and just forgot to.

>
> 129 + # This path probably does not exist
> 130 + # We can't test this on Windows, because it has a 'MIN_ABS_PATHLENGTH'
> 131 + # check...
> 132 + # self.assertRelpath('foo', '/', '/foo')
>
> Ideally this would be in a separate test method that could do a requireFeature check, I think. Not a big deal.

Sure, I could do that.

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iEYEARECAAYFAkrNX2IACgkQJdeBCYSNAAMQsQCfbgmfEneaDM3KffULtewF0w8w
VUsAnROBTldhwAIp+cEEhJRBfjYg6xS0
=e7ss
-----END PGP SIGNATURE-----

Revision history for this message
Andrew Bennetts (spiv) wrote :

John A Meinel wrote:
[...]
> > 83 + except OSError: # enoent
> >
> > That's a bit odd. Why not actually check that the exc.errno == ENOENT?
>
> Because I figured EPERM and EANYTHINGELSE would be just as reasonable.
> All we are doing here is trying to translate "foo" => "Foo" based on
> what is in the filesytem. If we can't access the filesystem, we have
> nothing to translate.

Ah ok. Then I'd just remove the “# enoent” comment, because it seems to
imply that you only care about that one errno.

-Andrew.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2009-10-07 12:01:20 +0000
3+++ NEWS 2009-10-08 17:16:09 +0000
4@@ -2,8 +2,8 @@
5 Bazaar Release Notes
6 ####################
7
8-2.0 series (not released)
9-#########################
10+2.0.1 (not released)
11+####################
12
13 Compatibility Breaks
14 ********************
15@@ -29,6 +29,21 @@
16 * Fixed ``ObjectNotLocked`` errors when doing some log and diff operations
17 on branches via a smart server. (Andrew Bennetts, #389413)
18
19+* Handle things like ``bzr add foo`` and ``bzr rm foo`` when the tree is
20+ at the root of a drive. ``osutils._cicp_canonical_relpath`` always
21+ assumed that ``abspath()`` returned a path that did not have a trailing
22+ ``/``, but that is not true when working at the root of the filesystem.
23+ (John Arbash Meinel, Jason Spashett, #322807)
24+
25+* Improve the time for ``bzr log DIR`` for 2a format repositories.
26+ We had been using the same code path as for <2a formats, which required
27+ iterating over all objects in all revisions.
28+ (John Arbash Meinel, #374730)
29+
30+* Make sure that we unlock the tree if we fail to create a TreeTransform
31+ object when doing a merge, and there is limbo, or pending-deletions
32+ directory. (Gary van der Merwe, #427773)
33+
34 * Occasional IndexError on renamed files have been fixed. Operations that
35 set a full inventory in the working tree will now go via the
36 apply_inventory_delta code path which is simpler and easier to
37@@ -37,6 +52,9 @@
38 but such operations are already doing full tree scans, so no radical
39 performance change should be observed. (Robert Collins, #403322)
40
41+* Retrieving file text or mtime from a _PreviewTree has good performance when
42+ there are many changes. (Aaron Bentley)
43+
44 * When a file kind becomes unversionable after being added, a sensible
45 error will be shown instead of a traceback. (Robert Collins, #438569)
46
47@@ -60,25 +78,6 @@
48 Testing
49 *******
50
51-bzr 2.0.1
52-##########
53-
54-Bug Fixes
55-*********
56-
57-* Improve the time for ``bzr log DIR`` for 2a format repositories.
58- We had been using the same code path as for <2a formats, which required
59- iterating over all objects in all revisions.
60- (John Arbash Meinel, #374730)
61-
62-* Make sure that we unlock the tree if we fail to create a TreeTransform
63- object when doing a merge, and there is limbo, or pending-deletions
64- directory. (Gary van der Merwe, #427773)
65-
66-* Retrieving file text or mtime from a _PreviewTree has good performance when
67- there are many changes. (Aaron Bentley)
68-
69-
70 bzr 2.0.0
71 #########
72
73
74=== modified file 'bzrlib/osutils.py'
75--- bzrlib/osutils.py 2009-07-23 16:01:17 +0000
76+++ bzrlib/osutils.py 2009-10-08 17:16:09 +0000
77@@ -1083,7 +1083,14 @@
78 bit_iter = iter(rel.split('/'))
79 for bit in bit_iter:
80 lbit = bit.lower()
81- for look in _listdir(current):
82+ try:
83+ next_entries = _listdir(current)
84+ except OSError: # enoent, eperm, etc
85+ # We can't find this in the filesystem, so just append the
86+ # remaining bits.
87+ current = pathjoin(current, bit, *list(bit_iter))
88+ break
89+ for look in next_entries:
90 if lbit == look.lower():
91 current = pathjoin(current, look)
92 break
93@@ -1093,7 +1100,7 @@
94 # the target of a move, for example).
95 current = pathjoin(current, bit, *list(bit_iter))
96 break
97- return current[len(abs_base)+1:]
98+ return current[len(abs_base):].lstrip('/')
99
100 # XXX - TODO - we need better detection/integration of case-insensitive
101 # file-systems; Linux often sees FAT32 devices (or NFS-mounted OSX
102
103=== modified file 'bzrlib/tests/test_osutils.py'
104--- bzrlib/tests/test_osutils.py 2009-07-23 16:01:17 +0000
105+++ bzrlib/tests/test_osutils.py 2009-10-08 17:16:09 +0000
106@@ -460,6 +460,49 @@
107 self.failUnlessEqual('work/MixedCaseParent/nochild', actual)
108
109
110+class Test_CICPCanonicalRelpath(tests.TestCaseWithTransport):
111+
112+ def assertRelpath(self, expected, base, path):
113+ actual = osutils._cicp_canonical_relpath(base, path)
114+ self.assertEqual(expected, actual)
115+
116+ def test_simple(self):
117+ self.build_tree(['MixedCaseName'])
118+ base = osutils.realpath(self.get_transport('.').local_abspath('.'))
119+ self.assertRelpath('MixedCaseName', base, 'mixedcAsename')
120+
121+ def test_subdir_missing_tail(self):
122+ self.build_tree(['MixedCaseParent/', 'MixedCaseParent/a_child'])
123+ base = osutils.realpath(self.get_transport('.').local_abspath('.'))
124+ self.assertRelpath('MixedCaseParent/a_child', base,
125+ 'MixedCaseParent/a_child')
126+ self.assertRelpath('MixedCaseParent/a_child', base,
127+ 'MixedCaseParent/A_Child')
128+ self.assertRelpath('MixedCaseParent/not_child', base,
129+ 'MixedCaseParent/not_child')
130+
131+ def test_at_root_slash(self):
132+ # We can't test this on Windows, because it has a 'MIN_ABS_PATHLENGTH'
133+ # check...
134+ if osutils.MIN_ABS_PATHLENGTH > 1:
135+ raise tests.TestSkipped('relpath requires %d chars'
136+ % osutils.MIN_ABS_PATHLENGTH)
137+ self.assertRelpath('foo', '/', '/foo')
138+
139+ def test_at_root_drive(self):
140+ if sys.platform != 'win32':
141+ raise tests.TestNotApplicable('we can only test drive-letter relative'
142+ ' paths on Windows where we have drive'
143+ ' letters.')
144+ # see bug #322807
145+ # The specific issue is that when at the root of a drive, 'abspath'
146+ # returns "C:/" or just "/". However, the code assumes that abspath
147+ # always returns something like "C:/foo" or "/foo" (no trailing slash).
148+ self.assertRelpath('foo', 'C:/', 'C:/foo')
149+ self.assertRelpath('foo', 'X:/', 'X:/foo')
150+ self.assertRelpath('foo', 'X:/', 'X://foo')
151+
152+
153 class TestPumpFile(tests.TestCase):
154 """Test pumpfile method."""
155

Subscribers

People subscribed via source and target branches