Skip to content

Instantly share code, notes, and snippets.

@mistydemeo
Created September 6, 2012 20:21
Show Gist options
  • Save mistydemeo/3660108 to your computer and use it in GitHub Desktop.
Save mistydemeo/3660108 to your computer and use it in GitHub Desktop.
require 'fileutils'
require 'tmpdir'
# FileUtils.mv
#
# Ruby 1.8.7's FileUtils.mv calls File.rename on the src. However, File.rename
# raises Errno::EXDEV when src and dst exist on separate devices. The fallback
# is to perform a copy instead of a rename.
#
# However, there is a bug in 1.8.7's implementation. If src is a symlink or a
# directory containing a symlink, and the target of that symlink has not yet been
# copied, the copy will fail, because copy_metadata calls File.utime on the
# symlink. File.utime resolves symlinks, and because the target does yet exist,
# it raises Errno::ENOENT.
#
# This was fixed in Ruby here:
# https://github.com/ruby/ruby/commit/7d89ecdc523aab7b9908501b6743da2c26a53825
# ***NOTE***: This is an entirely different problem than the one that prompted
# the introduction of HOMEBREW_TEMP. In that case, the BSD mv(1) command bails
# out if we're moving a symlink across volumes when the target has *already*
# been moved. We only see this when Pathname#install_p is fed a symlink directly,
# as that is the only case in which we exec mv(1) rather than using FileUtils.mv.
if RUBY_VERSION.to_f < 1.9
class FileUtils::Entry_
def copy_metadata(path)
st = lstat()
if !st.symlink?
File.utime st.atime, st.mtime, path
end
begin
if st.symlink?
begin
File.lchown st.uid, st.gid, path
rescue NotImplementedError
end
else
File.chown st.uid, st.gid, path
end
rescue Errno::EPERM
# clear setuid/setgid
if st.symlink?
begin
File.lchmod st.mode & 01777, path
rescue NotImplementedError
end
else
File.chmod st.mode & 01777, path
end
else
if st.symlink?
begin
File.lchmod st.mode, path
rescue NotImplementedError
end
else
File.chmod st.mode, path
end
end
end
end
end
begin
Kernel.system "diskutil erasevolume HFS+ 'fileutilstmp' $(hdiutil attach -nomount ram://1024)"
Dir.mktmpdir do |dir|
Dir.chdir dir do
FileUtils.mkdir 'foo'
FileUtils.mkdir 'bar'
FileUtils.ln_s '../bar', 'foo/bar'
begin
FileUtils.mv 'foo', '/Volumes/fileutilstmp'
rescue Errno::ENOENT
puts "Ruby 1.8.7's FileUtils.mv blows."
end
end
end
ensure
Kernel.system "umount", "/Volumes/fileutilstmp"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment