Skip to content

Commit 55e6cf9

Browse files
mamehsbt
authored andcommitted
Add FileUtils#cp_lr
* lib/fileutils.rb: Add FileUtils#cp_lr. This method creates hard links of each file from directory to another directory recursively. This patch is based on Thomas Sawyers and Zachary Scott. [Feature #4189] [ruby-core:33820] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@62739 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1 parent 40b5123 commit 55e6cf9

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

lib/fileutils.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,39 @@ def ln(src, dest, force: nil, noop: nil, verbose: nil)
298298
alias link ln
299299
module_function :link
300300

301+
#
302+
# :call-seq:
303+
# FileUtils.cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false)
304+
#
305+
# Hard link +src+ to +dest+. If +src+ is a directory, this method links
306+
# all its contents recursively. If +dest+ is a directory, links
307+
# +src+ to +dest/src+.
308+
#
309+
# +src+ can be a list of files.
310+
#
311+
# # Installing ruby library "mylib" under the site_ruby
312+
# FileUtils.rm_r site_ruby + '/mylib', :force => true
313+
# FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
314+
#
315+
# # Examples of copying several files to target directory.
316+
# FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
317+
# FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
318+
#
319+
# # If you want to copy all contents of a directory instead of the
320+
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
321+
# # use following code.
322+
# FileUtils.cp_lr 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest, but this doesn't.
323+
#
324+
def cp_lr(src, dest, noop: nil, verbose: nil,
325+
dereference_root: true, remove_destination: false)
326+
fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose
327+
return if noop
328+
fu_each_src_dest(src, dest) do |s, d|
329+
link_entry s, d, dereference_root, remove_destination
330+
end
331+
end
332+
module_function :cp_lr
333+
301334
#
302335
# :call-seq:
303336
# FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil)
@@ -344,6 +377,26 @@ def ln_sf(src, dest, noop: nil, verbose: nil)
344377
end
345378
module_function :ln_sf
346379

380+
#
381+
# Hard links a file system entry +src+ to +dest+.
382+
# If +src+ is a directory, this method links its contents recursively.
383+
#
384+
# Both of +src+ and +dest+ must be a path name.
385+
# +src+ must exist, +dest+ must not exist.
386+
#
387+
# If +dereference_root+ is true, this method dereference tree root.
388+
#
389+
# If +remove_destination+ is true, this method removes each destination file before copy.
390+
#
391+
def link_entry(src, dest, dereference_root = false, remove_destination = false)
392+
Entry_.new(src, nil, dereference_root).traverse do |ent|
393+
destent = Entry_.new(dest, ent.rel, false)
394+
File.unlink destent.path if remove_destination && File.file?(destent.path)
395+
ent.link destent.path
396+
end
397+
end
398+
module_function :link_entry
399+
347400
#
348401
# Copies a file content +src+ to +dest+. If +dest+ is a directory,
349402
# copies +src+ to +dest/src+.
@@ -1278,6 +1331,22 @@ def chown(uid, gid)
12781331
end
12791332
end
12801333

1334+
def link(dest)
1335+
case
1336+
when directory?
1337+
if !File.exist?(dest) and descendant_directory?(dest, path)
1338+
raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest]
1339+
end
1340+
begin
1341+
Dir.mkdir dest
1342+
rescue
1343+
raise unless File.directory?(dest)
1344+
end
1345+
else
1346+
File.link path(), dest
1347+
end
1348+
end
1349+
12811350
def copy(dest)
12821351
lstat
12831352
case

test/fileutils/test_fileutils.rb

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,45 @@ def test_cp_r_symlink_remove_destination
449449
cp_r 'tmp/src', 'tmp/dest/', remove_destination: true
450450
end if have_symlink?
451451

452+
def test_cp_lr
453+
check_singleton :cp_lr
454+
455+
cp_lr 'data', 'tmp'
456+
TARGETS.each do |fname|
457+
assert_same_file fname, "tmp/#{fname}"
458+
end
459+
460+
# a/* -> b/*
461+
mkdir 'tmp/cpr_src'
462+
mkdir 'tmp/cpr_dest'
463+
File.open('tmp/cpr_src/a', 'w') {|f| f.puts 'a' }
464+
File.open('tmp/cpr_src/b', 'w') {|f| f.puts 'b' }
465+
File.open('tmp/cpr_src/c', 'w') {|f| f.puts 'c' }
466+
mkdir 'tmp/cpr_src/d'
467+
cp_lr 'tmp/cpr_src/.', 'tmp/cpr_dest'
468+
assert_same_file 'tmp/cpr_src/a', 'tmp/cpr_dest/a'
469+
assert_same_file 'tmp/cpr_src/b', 'tmp/cpr_dest/b'
470+
assert_same_file 'tmp/cpr_src/c', 'tmp/cpr_dest/c'
471+
assert_directory 'tmp/cpr_dest/d'
472+
my_rm_rf 'tmp/cpr_src'
473+
my_rm_rf 'tmp/cpr_dest'
474+
475+
bug3588 = '[ruby-core:31360]'
476+
mkdir 'tmp2'
477+
assert_nothing_raised(ArgumentError, bug3588) do
478+
cp_lr 'tmp', 'tmp2'
479+
end
480+
assert_directory 'tmp2/tmp'
481+
assert_raise(ArgumentError, bug3588) do
482+
cp_lr 'tmp2', 'tmp2/new_tmp2'
483+
end
484+
485+
bug12892 = '[ruby-core:77885] [Bug #12892]'
486+
assert_raise(Errno::ENOENT, bug12892) do
487+
cp_lr 'non/existent', 'tmp'
488+
end
489+
end if have_hardlink?
490+
452491
def test_mv
453492
check_singleton :mv
454493

0 commit comments

Comments
 (0)