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
=== modified file 'NEWS'
--- NEWS 2010-07-19 08:47:42 +0000
+++ NEWS 2010-07-19 08:47:42 +0000
@@ -18,6 +18,13 @@
18 history no longer crash when deleted files are involved.18 history no longer crash when deleted files are involved.
19 (Vincent Ladeuil, John Arbash Meinel, #375898)19 (Vincent Ladeuil, John Arbash Meinel, #375898)
2020
21* ``bzr add SYMLINK_TO_BRANCH`` and other commands now work on the
22 symlink, not on the branch that it points to. (Specifically
23 ``BzrDir.open_containing`` if the last component of the path is a
24 symlink, will open the directory containing that symlink rather than the
25 directory that it points to.)
26 (Martin Pool, #32669)
27
21* ``bzr commit SYMLINK`` now works, rather than trying to commit the28* ``bzr commit SYMLINK`` now works, rather than trying to commit the
22 target of the symlink.29 target of the symlink.
23 (Martin Pool, John Arbash Meinel, #128562)30 (Martin Pool, John Arbash Meinel, #128562)
2431
=== modified file 'bzrlib/bzrdir.py'
--- bzrlib/bzrdir.py 2010-02-18 04:45:24 +0000
+++ bzrlib/bzrdir.py 2010-07-19 08:47:42 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd1# Copyright (C) 2005-2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
28# TODO: Move old formats into a plugin to make this file smaller.28# TODO: Move old formats into a plugin to make this file smaller.
2929
30import os30import os
31import stat
31import sys32import sys
3233
33from bzrlib.lazy_import import lazy_import34from bzrlib.lazy_import import lazy_import
@@ -908,26 +909,48 @@
908 format, UnknownFormatError or UnsupportedFormatError are raised.909 format, UnknownFormatError or UnsupportedFormatError are raised.
909 If there is one, it is returned, along with the unused portion of url.910 If there is one, it is returned, along with the unused portion of url.
910911
912 If the transport points directly to a symlink, we don't follow it, but
913 rather walk up to the containing directory. This is so that you can
914 operate on a symlink within one branch that points to another branch.
915 This is applied only on the last component of the name, not every step
916 up.
917
911 :return: The BzrDir that contains the path, and a Unicode path918 :return: The BzrDir that contains the path, and a Unicode path
912 for the rest of the URL.919 for the rest of the URL.
913 """920 """
914 # this gets the normalised url back. I.e. '.' -> the full path.921 def go_up(t):
915 url = a_transport.base922 try:
916 while True:923 new_t = t.clone('..')
917 try:924 mutter("walk up to %s" % new_t)
918 result = BzrDir.open_from_transport(a_transport)925 except errors.InvalidURLJoin:
919 return result, urlutils.unescape(a_transport.relpath(url))926 # reached the root, whatever that may be
927 raise errors.NotBranchError(path=t.base)
928 if new_t.base == t.base:
929 # reached the root, whatever that may be
930 raise errors.NotBranchError(path=t.base)
931 return probe(new_t)
932 def probe(t):
933 try:
934 return BzrDir.open_from_transport(t)
920 except errors.NotBranchError, e:935 except errors.NotBranchError, e:
921 pass936 return go_up(t)
937 def initial_probe(t):
922 try:938 try:
923 new_t = a_transport.clone('..')939 # nb: this depends on 'a'.stat('') mapping to stat('a') but it
924 except errors.InvalidURLJoin:940 # does
925 # reached the root, whatever that may be941 if stat.S_ISLNK(t.stat('').st_mode):
926 raise errors.NotBranchError(path=url)942 # not going to even try opening this; continue upwards
927 if new_t.base == a_transport.base:943 mutter("found symlink at %r, walking up" % t)
928 # reached the root, whatever that may be944 return go_up(t)
929 raise errors.NotBranchError(path=url)945 except errors.TransportNotPossible:
930 a_transport = new_t946 pass
947 except errors.NoSuchFile:
948 pass
949 mutter("didn't find symlink at %r" % t)
950 return probe(a_transport)
951 bd = initial_probe(a_transport)
952 return bd, urlutils.unescape(
953 bd.root_transport.relpath(a_transport.base))
931954
932 def _get_tree_branch(self):955 def _get_tree_branch(self):
933 """Return the branch and tree, if any, for this bzrdir.956 """Return the branch and tree, if any, for this bzrdir.
934957
=== modified file 'bzrlib/tests/blackbox/test_add.py'
--- bzrlib/tests/blackbox/test_add.py 2009-08-10 08:25:05 +0000
+++ bzrlib/tests/blackbox/test_add.py 2010-07-19 08:47:42 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005, 2006, 2007, 2009 Canonical Ltd1# Copyright (C) 2005, 2006, 2007, 2009, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -19,7 +19,10 @@
1919
20import os20import os
2121
22from bzrlib import osutils22from bzrlib import (
23 osutils,
24 tests,
25 )
23from bzrlib.tests import (26from bzrlib.tests import (
24 condition_isinstance,27 condition_isinstance,
25 split_suite_by_condition,28 split_suite_by_condition,
@@ -225,8 +228,12 @@
225 self.run_bzr(['add', u'\u1234?', u'\u1235*'])228 self.run_bzr(['add', u'\u1234?', u'\u1235*'])
226 self.assertEquals(self.run_bzr('unknowns')[0], 'cc\n')229 self.assertEquals(self.run_bzr('unknowns')[0], 'cc\n')
227230
231
232class TestAddSymlinks(tests.TestCaseWithTransport):
233
234 _test_needs_features = [tests.SymlinkFeature]
235
228 def test_add_via_symlink(self):236 def test_add_via_symlink(self):
229 self.requireFeature(SymlinkFeature)
230 self.make_branch_and_tree('source')237 self.make_branch_and_tree('source')
231 self.build_tree(['source/top.txt'])238 self.build_tree(['source/top.txt'])
232 os.symlink('source', 'link')239 os.symlink('source', 'link')
@@ -234,8 +241,23 @@
234 self.assertEquals(out, 'adding top.txt\n')241 self.assertEquals(out, 'adding top.txt\n')
235242
236 def test_add_symlink_to_abspath(self):243 def test_add_symlink_to_abspath(self):
237 self.requireFeature(SymlinkFeature)
238 self.make_branch_and_tree('tree')244 self.make_branch_and_tree('tree')
239 os.symlink(osutils.abspath('target'), 'tree/link')245 os.symlink(osutils.abspath('target'), 'tree/link')
240 out = self.run_bzr(['add', 'tree/link'])[0]246 out = self.run_bzr(['add', 'tree/link'])[0]
241 self.assertEquals(out, 'adding link\n')247 self.assertEquals(out, 'adding link\n')
248
249 def test_smart_add_symlink_to_branch(self):
250 # add of a symlink pointing to another branch should add the symlink,
251 # not the files in the other branch
252 # https://bugs.edge.launchpad.net/bzr/+bug/32669
253 tree1 = self.make_branch_and_tree('tree1')
254 tree2 = self.make_branch_and_tree('tree2')
255 self.build_tree_contents([
256 ('tree1/link@', '../tree2'),
257 ('tree2/file', 'file'),
258 ('tree2/evil@', '../tree1'),
259 ])
260 out, err = self.run_bzr(['add', 'tree1/link'])
261 # if this fails, it's probably because add opened the wrong working
262 # tree, which is tested in test_bzrdir
263 self.assertEquals(out, 'adding link\n')
242264
=== modified file 'bzrlib/tests/test_bzrdir.py'
--- bzrlib/tests/test_bzrdir.py 2009-07-10 07:14:02 +0000
+++ bzrlib/tests/test_bzrdir.py 2010-07-19 08:47:42 +0000
@@ -1,4 +1,4 @@
1# Copyright (C) 2005, 2006, 2007 Canonical Ltd1# Copyright (C) 2005, 2006, 2007, 2010 Canonical Ltd
2#2#
3# This program is free software; you can redistribute it and/or modify3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by4# it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@
30 repository,30 repository,
31 osutils,31 osutils,
32 remote,32 remote,
33 tests,
33 urlutils,34 urlutils,
34 win32utils,35 win32utils,
35 workingtree,36 workingtree,
@@ -1325,3 +1326,30 @@
1325 url = transport.base1326 url = transport.base
1326 err = self.assertRaises(errors.BzrError, bzrdir.BzrDir.open, url)1327 err = self.assertRaises(errors.BzrError, bzrdir.BzrDir.open, url)
1327 self.assertEqual('fail', err._preformatted_string)1328 self.assertEqual('fail', err._preformatted_string)
1329
1330
1331class TestSymlinks(TestCaseWithTransport):
1332 """Tests for opening bzrdirs named by symlinks.
1333
1334 See bug 32669 and friends.
1335
1336 See also tests.blackbox.test_add
1337 """
1338
1339 _test_needs_features = [tests.SymlinkFeature]
1340
1341 def test_open_named_by_symlink(self):
1342 """A symlink within a tree pointing to another tree.
1343
1344 Treated primarily as a symlink, not indirected.
1345 """
1346 tree1 = self.make_branch_and_tree('tree1')
1347 tree2 = self.make_branch_and_tree('tree2')
1348 self.build_tree_contents([
1349 ('tree1/link@', '../tree2'),
1350 ])
1351 link_url = tree1.abspath('link')
1352 control, relpath = bzrdir.BzrDir.open_containing(
1353 link_url, [tree1._transport, tree2._transport])
1354 self.assertEquals(control.transport.base, tree1.bzrdir.transport.base)
1355 self.assertEquals(relpath, 'link')

Subscribers

People subscribed via source and target branches