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

Proposed by Martin Pool
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 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.
Revision history for this message
Martin Pool (mbp) wrote :

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

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

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

Unmerged revisions

4758. By Martin Pool

open_containing opens the directory containing symlinks, not the target

4757. By Martin Pool

Remove typo from test

4756. By Martin Pool

Merge transport symlink support

4755. By Martin Pool

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