Skip to content

Commit 1080ee0

Browse files
authored
Merge pull request #136 from nobu/ln_sr
Refine `ln_sr`
2 parents 7a748eb + 277f7f2 commit 1080ee0

File tree

2 files changed

+42
-26
lines changed

2 files changed

+42
-26
lines changed

lib/fileutils.rb

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -731,30 +731,34 @@ def ln_sf(src, dest, noop: nil, verbose: nil)
731731
# Like FileUtils.ln_s, but create links relative to +dest+.
732732
#
733733
def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
734-
fu_output_message "ln -sr#{force ? 'f' : ''}#{
735-
target_directory ? '' : 'T'} #{[src,dest].flatten.join ' '}" if verbose
736-
return if noop
737-
unless target_directory
738-
destdirs = fu_split_path(File.realdirpath(dest))
739-
end
734+
cmd = "ln -s#{force ? 'f' : ''}#{target_directory ? '' : 'T'}" if verbose
740735
fu_each_src_dest0(src, dest, target_directory) do |s,d|
741736
if target_directory
742-
destdirs = fu_split_path(File.realdirpath(File.dirname(d)))
743-
# else d == dest
737+
parent = File.dirname(d)
738+
destdirs = fu_split_path(parent)
739+
real_ddirs = fu_split_path(File.realpath(parent))
740+
else
741+
destdirs ||= fu_split_path(dest)
742+
real_ddirs ||= fu_split_path(File.realpath(dest))
744743
end
745-
if fu_starting_path?(s)
746-
srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
747-
base = fu_relative_components_from(srcdirs, destdirs)
748-
s = File.join(*base)
744+
srcdirs = fu_split_path(s)
745+
i = fu_common_components(srcdirs, destdirs)
746+
n = destdirs.size - i
747+
n -= 1 unless target_directory
748+
link1 = fu_clean_components(*Array.new([n, 0].max, '..'), *srcdirs[i..-1])
749+
begin
750+
real_sdirs = fu_split_path(File.realdirpath(s)) rescue nil
751+
rescue
749752
else
750-
srcdirs = fu_clean_components(*fu_split_path(s))
751-
base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
752-
while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
753-
srcdirs.shift
754-
base.pop
755-
end
756-
s = File.join(*base, *srcdirs)
753+
i = fu_common_components(real_sdirs, real_ddirs)
754+
n = real_ddirs.size - i
755+
n -= 1 unless target_directory
756+
link2 = fu_clean_components(*Array.new([n, 0].max, '..'), *real_sdirs[i..-1])
757+
link1 = link2 if link1.size > link2.size
757758
end
759+
s = File.join(link1)
760+
fu_output_message [cmd, s, d].flatten.join(' ') if verbose
761+
next if noop
758762
remove_file d, true if force
759763
File.symlink s, d
760764
end
@@ -2504,22 +2508,26 @@ def fu_split_path(path) #:nodoc:
25042508
path = File.path(path)
25052509
list = []
25062510
until (parent, base = File.split(path); parent == path or parent == ".")
2507-
list << base
2511+
if base != '..' and list.last == '..' and !(fu_have_symlink? && File.symlink?(path))
2512+
list.pop
2513+
else
2514+
list << base
2515+
end
25082516
path = parent
25092517
end
25102518
list << path
25112519
list.reverse!
25122520
end
25132521
private_module_function :fu_split_path
25142522

2515-
def fu_relative_components_from(target, base) #:nodoc:
2523+
def fu_common_components(target, base) #:nodoc:
25162524
i = 0
25172525
while target[i]&.== base[i]
25182526
i += 1
25192527
end
2520-
Array.new(base.size-i, '..').concat(target[i..-1])
2528+
i
25212529
end
2522-
private_module_function :fu_relative_components_from
2530+
private_module_function :fu_common_components
25232531

25242532
def fu_clean_components(*comp) #:nodoc:
25252533
comp.shift while comp.first == "."
@@ -2529,7 +2537,7 @@ def fu_clean_components(*comp) #:nodoc:
25292537
while c = comp.shift
25302538
if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
25312539
clean.pop
2532-
path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
2540+
path.sub!(%r((?<=\A|/)[^/]+/\z), "")
25332541
else
25342542
clean << c
25352543
path << c << "/"

test/fileutils/test_fileutils.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,8 @@ def test_ln_sr
10311031
assert_all_assertions_foreach(nil, *TARGETS) do |fname|
10321032
lnfname = 'tmp/lnsdest'
10331033
ln_sr fname, lnfname
1034-
assert FileTest.symlink?(lnfname), 'not symlink'
1034+
assert_file.symlink?(lnfname)
1035+
assert_file.identical?(lnfname, fname)
10351036
assert_equal "../#{fname}", File.readlink(lnfname)
10361037
ensure
10371038
rm_f lnfname
@@ -1048,12 +1049,19 @@ def test_ln_sr
10481049
end
10491050
end
10501051

1052+
File.symlink 'data', 'link'
1053+
mkdir 'link/d1'
1054+
mkdir 'link/d2'
1055+
ln_sr 'link/d1/z', 'link/d2'
1056+
assert_equal '../d1/z', File.readlink('data/d2/z')
1057+
10511058
mkdir 'data/src'
10521059
File.write('data/src/xxx', 'ok')
10531060
File.symlink '../data/src', 'tmp/src'
10541061
ln_sr 'tmp/src/xxx', 'data'
1055-
assert File.symlink?('data/xxx')
1062+
assert_file.symlink?('data/xxx')
10561063
assert_equal 'ok', File.read('data/xxx')
1064+
assert_equal 'src/xxx', File.readlink('data/xxx')
10571065
end
10581066

10591067
def test_ln_sr_not_target_directory

0 commit comments

Comments
 (0)