Merge lp:~mbp/bzr/32669-2.0-symlink-branch into lp:bzr/2.0

Proposed by Martin Pool on 2010-07-19
Status: Work in progress
Proposed branch: lp:~mbp/bzr/32669-2.0-symlink-branch
Merge into: lp:bzr/2.0
Prerequisite: lp:~mbp/bzr/2.0-stat-symlink
Diff against target: 209 lines (+101/-21)
4 files modified
NEWS (+7/-0)
bzrlib/bzrdir.py (+39/-16)
bzrlib/tests/blackbox/test_add.py (+26/-4)
bzrlib/tests/test_bzrdir.py (+29/-1)
To merge this branch: bzr merge lp:~mbp/bzr/32669-2.0-symlink-branch
Reviewer Review Type Date Requested Status
bzr-core 2010-07-19 Pending
Review via email: mp+30241@code.launchpad.net

Description of the Change

This fixes 'bzr add SYMLINK_TO_BRANCH' (bug 32669) to add the symlink, rather than running add in the referent branch. The basic fix is that when you say "open_containing_from_transport", we shouldn't follow a symlink in the last part.

To post a comment you must log in.
Martin Pool (mbp) wrote :

I'm not sure this is passing tests yet so not asking for review.

Vincent Ladeuil (vila) wrote :

@Martin: ping.
I just cut 2.0.6, is this abandoned ?

Unmerged revisions

4758. By Martin Pool on 2010-07-18

open_containing opens the directory containing symlinks, not the target

4757. By Martin Pool on 2010-07-18

Remove typo from test

4756. By Martin Pool on 2010-07-18

Merge transport symlink support

4755. By Martin Pool on 2010-07-17

Add test for bug 32669

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-07-19 08:47:42 +0000
3+++ NEWS 2010-07-19 08:47:42 +0000
4@@ -18,6 +18,13 @@
5 history no longer crash when deleted files are involved.
6 (Vincent Ladeuil, John Arbash Meinel, #375898)
7
8+* ``bzr add SYMLINK_TO_BRANCH`` and other commands now work on the
9+ symlink, not on the branch that it points to. (Specifically
10+ ``BzrDir.open_containing`` if the last component of the path is a
11+ symlink, will open the directory containing that symlink rather than the
12+ directory that it points to.)
13+ (Martin Pool, #32669)
14+
15 * ``bzr commit SYMLINK`` now works, rather than trying to commit the
16 target of the symlink.
17 (Martin Pool, John Arbash Meinel, #128562)
18
19=== modified file 'bzrlib/bzrdir.py'
20--- bzrlib/bzrdir.py 2010-02-18 04:45:24 +0000
21+++ bzrlib/bzrdir.py 2010-07-19 08:47:42 +0000
22@@ -1,4 +1,4 @@
23-# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
24+# Copyright (C) 2005-2010 Canonical Ltd
25 #
26 # This program is free software; you can redistribute it and/or modify
27 # it under the terms of the GNU General Public License as published by
28@@ -28,6 +28,7 @@
29 # TODO: Move old formats into a plugin to make this file smaller.
30
31 import os
32+import stat
33 import sys
34
35 from bzrlib.lazy_import import lazy_import
36@@ -908,26 +909,48 @@
37 format, UnknownFormatError or UnsupportedFormatError are raised.
38 If there is one, it is returned, along with the unused portion of url.
39
40+ If the transport points directly to a symlink, we don't follow it, but
41+ rather walk up to the containing directory. This is so that you can
42+ operate on a symlink within one branch that points to another branch.
43+ This is applied only on the last component of the name, not every step
44+ up.
45+
46 :return: The BzrDir that contains the path, and a Unicode path
47 for the rest of the URL.
48 """
49- # this gets the normalised url back. I.e. '.' -> the full path.
50- url = a_transport.base
51- while True:
52- try:
53- result = BzrDir.open_from_transport(a_transport)
54- return result, urlutils.unescape(a_transport.relpath(url))
55+ def go_up(t):
56+ try:
57+ new_t = t.clone('..')
58+ mutter("walk up to %s" % new_t)
59+ except errors.InvalidURLJoin:
60+ # reached the root, whatever that may be
61+ raise errors.NotBranchError(path=t.base)
62+ if new_t.base == t.base:
63+ # reached the root, whatever that may be
64+ raise errors.NotBranchError(path=t.base)
65+ return probe(new_t)
66+ def probe(t):
67+ try:
68+ return BzrDir.open_from_transport(t)
69 except errors.NotBranchError, e:
70- pass
71+ return go_up(t)
72+ def initial_probe(t):
73 try:
74- new_t = a_transport.clone('..')
75- except errors.InvalidURLJoin:
76- # reached the root, whatever that may be
77- raise errors.NotBranchError(path=url)
78- if new_t.base == a_transport.base:
79- # reached the root, whatever that may be
80- raise errors.NotBranchError(path=url)
81- a_transport = new_t
82+ # nb: this depends on 'a'.stat('') mapping to stat('a') but it
83+ # does
84+ if stat.S_ISLNK(t.stat('').st_mode):
85+ # not going to even try opening this; continue upwards
86+ mutter("found symlink at %r, walking up" % t)
87+ return go_up(t)
88+ except errors.TransportNotPossible:
89+ pass
90+ except errors.NoSuchFile:
91+ pass
92+ mutter("didn't find symlink at %r" % t)
93+ return probe(a_transport)
94+ bd = initial_probe(a_transport)
95+ return bd, urlutils.unescape(
96+ bd.root_transport.relpath(a_transport.base))
97
98 def _get_tree_branch(self):
99 """Return the branch and tree, if any, for this bzrdir.
100
101=== modified file 'bzrlib/tests/blackbox/test_add.py'
102--- bzrlib/tests/blackbox/test_add.py 2009-08-10 08:25:05 +0000
103+++ bzrlib/tests/blackbox/test_add.py 2010-07-19 08:47:42 +0000
104@@ -1,4 +1,4 @@
105-# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd
106+# Copyright (C) 2005, 2006, 2007, 2009, 2010 Canonical Ltd
107 #
108 # This program is free software; you can redistribute it and/or modify
109 # it under the terms of the GNU General Public License as published by
110@@ -19,7 +19,10 @@
111
112 import os
113
114-from bzrlib import osutils
115+from bzrlib import (
116+ osutils,
117+ tests,
118+ )
119 from bzrlib.tests import (
120 condition_isinstance,
121 split_suite_by_condition,
122@@ -225,8 +228,12 @@
123 self.run_bzr(['add', u'\u1234?', u'\u1235*'])
124 self.assertEquals(self.run_bzr('unknowns')[0], 'cc\n')
125
126+
127+class TestAddSymlinks(tests.TestCaseWithTransport):
128+
129+ _test_needs_features = [tests.SymlinkFeature]
130+
131 def test_add_via_symlink(self):
132- self.requireFeature(SymlinkFeature)
133 self.make_branch_and_tree('source')
134 self.build_tree(['source/top.txt'])
135 os.symlink('source', 'link')
136@@ -234,8 +241,23 @@
137 self.assertEquals(out, 'adding top.txt\n')
138
139 def test_add_symlink_to_abspath(self):
140- self.requireFeature(SymlinkFeature)
141 self.make_branch_and_tree('tree')
142 os.symlink(osutils.abspath('target'), 'tree/link')
143 out = self.run_bzr(['add', 'tree/link'])[0]
144 self.assertEquals(out, 'adding link\n')
145+
146+ def test_smart_add_symlink_to_branch(self):
147+ # add of a symlink pointing to another branch should add the symlink,
148+ # not the files in the other branch
149+ # https://bugs.edge.launchpad.net/bzr/+bug/32669
150+ tree1 = self.make_branch_and_tree('tree1')
151+ tree2 = self.make_branch_and_tree('tree2')
152+ self.build_tree_contents([
153+ ('tree1/link@', '../tree2'),
154+ ('tree2/file', 'file'),
155+ ('tree2/evil@', '../tree1'),
156+ ])
157+ out, err = self.run_bzr(['add', 'tree1/link'])
158+ # if this fails, it's probably because add opened the wrong working
159+ # tree, which is tested in test_bzrdir
160+ self.assertEquals(out, 'adding link\n')
161
162=== modified file 'bzrlib/tests/test_bzrdir.py'
163--- bzrlib/tests/test_bzrdir.py 2009-07-10 07:14:02 +0000
164+++ bzrlib/tests/test_bzrdir.py 2010-07-19 08:47:42 +0000
165@@ -1,4 +1,4 @@
166-# Copyright (C) 2005, 2006, 2007 Canonical Ltd
167+# Copyright (C) 2005, 2006, 2007, 2010 Canonical Ltd
168 #
169 # This program is free software; you can redistribute it and/or modify
170 # it under the terms of the GNU General Public License as published by
171@@ -30,6 +30,7 @@
172 repository,
173 osutils,
174 remote,
175+ tests,
176 urlutils,
177 win32utils,
178 workingtree,
179@@ -1325,3 +1326,30 @@
180 url = transport.base
181 err = self.assertRaises(errors.BzrError, bzrdir.BzrDir.open, url)
182 self.assertEqual('fail', err._preformatted_string)
183+
184+
185+class TestSymlinks(TestCaseWithTransport):
186+ """Tests for opening bzrdirs named by symlinks.
187+
188+ See bug 32669 and friends.
189+
190+ See also tests.blackbox.test_add
191+ """
192+
193+ _test_needs_features = [tests.SymlinkFeature]
194+
195+ def test_open_named_by_symlink(self):
196+ """A symlink within a tree pointing to another tree.
197+
198+ Treated primarily as a symlink, not indirected.
199+ """
200+ tree1 = self.make_branch_and_tree('tree1')
201+ tree2 = self.make_branch_and_tree('tree2')
202+ self.build_tree_contents([
203+ ('tree1/link@', '../tree2'),
204+ ])
205+ link_url = tree1.abspath('link')
206+ control, relpath = bzrdir.BzrDir.open_containing(
207+ link_url, [tree1._transport, tree2._transport])
208+ self.assertEquals(control.transport.base, tree1.bzrdir.transport.base)
209+ self.assertEquals(relpath, 'link')

Subscribers

People subscribed via source and target branches