# module Chef::ChefFS::CommandLine

### Public Class Methods

diff(pattern, old_root, new_root, recurse_depth, get_content) click to toggle source

    # File lib/chef/chef_fs/command_line.rb, line 131
    def self.diff(pattern, old_root, new_root, recurse_depth, get_content)
      Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.list_pairs(pattern, old_root, new_root), :flatten => true) do |old_entry, new_entry|
        diff_entries(old_entry, new_entry, recurse_depth, get_content)
      end
    end

diff_entries(old_entry, new_entry, recurse_depth, get_content) click to toggle source

Diff two known entries (could be files or dirs)

    # File lib/chef/chef_fs/command_line.rb, line 138
    def self.diff_entries(old_entry, new_entry, recurse_depth, get_content)
      # If both are directories
      if old_entry.dir?
        if new_entry.dir?
          if recurse_depth == 0
            return [ [ :common_subdirectories, old_entry, new_entry ] ]
          else
            return Chef::ChefFS::Parallelizer.parallelize(Chef::ChefFS::FileSystem.child_pairs(old_entry, new_entry), :flatten => true) do |old_child, new_child|
              Chef::ChefFS::CommandLine.diff_entries(old_child, new_child, recurse_depth ? recurse_depth - 1 : nil, get_content)
            end
          end

        # If old is a directory and new is a file
        elsif new_entry.exists?
          return [ [ :directory_to_file, old_entry, new_entry ] ]

        # If old is a directory and new does not exist
        elsif new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
          return [ [ :deleted, old_entry, new_entry ] ]

        # If the new entry does not and *cannot* exist, report that.
        else
          return [ [ :new_cannot_upload, old_entry, new_entry ] ]
        end

      # If new is a directory and old is a file
      elsif new_entry.dir?
        if old_entry.exists?
          return [ [ :file_to_directory, old_entry, new_entry ] ]

        # If new is a directory and old does not exist
        elsif old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
          return [ [ :added, old_entry, new_entry ] ]

        # If the new entry does not and *cannot* exist, report that.
        else
          return [ [ :old_cannot_upload, old_entry, new_entry ] ]
        end

      # Neither is a directory, so they are diffable with file diff
      else
        are_same, old_value, new_value = Chef::ChefFS::FileSystem.compare(old_entry, new_entry)
        if are_same
          if old_value == :none
            return [ [ :both_nonexistent, old_entry, new_entry ] ]
          else
            return [ [ :same, old_entry, new_entry ] ]
          end
        else
          if old_value == :none
            old_exists = false
          elsif old_value.nil?
            old_exists = old_entry.exists?
          else
            old_exists = true
          end

          if new_value == :none
            new_exists = false
          elsif new_value.nil?
            new_exists = new_entry.exists?
          else
            new_exists = true
          end

          # If one of the files doesn't exist, we only want to print the diff if the
          # other file *could be uploaded/downloaded*.
          if !old_exists && !old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
            return [ [ :old_cannot_upload, old_entry, new_entry ] ]
          end
          if !new_exists && !new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
            return [ [ :new_cannot_upload, old_entry, new_entry ] ]
          end

          if get_content
            # If we haven't read the values yet, get them now so that they can be diffed
            begin
              old_value = old_entry.read if old_value.nil?
            rescue Chef::ChefFS::FileSystem::NotFoundError
              old_value = :none
            end
            begin
              new_value = new_entry.read if new_value.nil?
            rescue Chef::ChefFS::FileSystem::NotFoundError
              new_value = :none
            end
          end

          if old_value == :none || (old_value == nil && !old_entry.exists?)
            return [ [ :added, old_entry, new_entry, old_value, new_value ] ]
          elsif new_value == :none
            return [ [ :deleted, old_entry, new_entry, old_value, new_value ] ]
          else
            return [ [ :modified, old_entry, new_entry, old_value, new_value ] ]
          end
        end
      end
    rescue Chef::ChefFS::FileSystem::FileSystemError => e
      return [ [ :error, old_entry, new_entry, nil, nil, e ] ]
    end

diff_print(pattern, a_root, b_root, recurse_depth, output_mode, format_path = nil, diff_filter = nil, ui = nil) { |"Common subdirectories:| ... } click to toggle source

    # File lib/chef/chef_fs/command_line.rb, line 28
    def self.diff_print(pattern, a_root, b_root, recurse_depth, output_mode, format_path = nil, diff_filter = nil, ui = nil)
      if format_path.nil?
        format_path = proc { |entry| entry.path_for_printing }
      end

      get_content = (output_mode != :name_only && output_mode != :name_status)
      found_match = false
      diff(pattern, a_root, b_root, recurse_depth, get_content).each do |type, old_entry, new_entry, old_value, new_value, error|
        found_match = true unless type == :both_nonexistent
        old_path = format_path.call(old_entry)
        new_path = format_path.call(new_entry)

        case type
        when :common_subdirectories
          if output_mode != :name_only && output_mode != :name_status
            yield "Common subdirectories: #{new_path}\n"
          end

        when :directory_to_file
          next if diff_filter && diff_filter !~ /T/
          if output_mode == :name_only
            yield "#{new_path}\n"
          elsif output_mode == :name_status
            yield "T\t#{new_path}\n"
          else
            yield "File #{old_path} is a directory while file #{new_path} is a regular file\n"
          end

        when :file_to_directory
          next if diff_filter && diff_filter !~ /T/
          if output_mode == :name_only
            yield "#{new_path}\n"
          elsif output_mode == :name_status
            yield "T\t#{new_path}\n"
          else
            yield "File #{old_path} is a regular file while file #{new_path} is a directory\n"
          end

        when :deleted
          next if diff_filter && diff_filter !~ /D/
          if output_mode == :name_only
            yield "#{new_path}\n"
          elsif output_mode == :name_status
            yield "D\t#{new_path}\n"
          elsif old_value
            result = "diff --knife #{old_path} #{new_path}\n"
            result << "deleted file\n"
            result << diff_text(old_path, '/dev/null', old_value, '')
            yield result
          else
            yield "Only in #{format_path.call(old_entry.parent)}: #{old_entry.name}\n"
          end

        when :added
          next if diff_filter && diff_filter !~ /A/
          if output_mode == :name_only
            yield "#{new_path}\n"
          elsif output_mode == :name_status
            yield "A\t#{new_path}\n"
          elsif new_value
            result = "diff --knife #{old_path} #{new_path}\n"
            result << "new file\n"
            result << diff_text('/dev/null', new_path, '', new_value)
            yield result
          else
            yield "Only in #{format_path.call(new_entry.parent)}: #{new_entry.name}\n"
          end

        when :modified
          next if diff_filter && diff_filter !~ /M/
          if output_mode == :name_only
            yield "#{new_path}\n"
          elsif output_mode == :name_status
            yield "M\t#{new_path}\n"
          else
            result = "diff --knife #{old_path} #{new_path}\n"
            result << diff_text(old_path, new_path, old_value, new_value)
            yield result
          end

        when :both_nonexistent
        when :added_cannot_upload
        when :deleted_cannot_download
        when :same
          # Skip these silently
        when :error
          if error.is_a?(Chef::ChefFS::FileSystem::OperationFailedError)
            ui.error "#{format_path.call(error.entry)} failed to #{error.operation}: #{error.message}" if ui
            error = true
          elsif error.is_a?(Chef::ChefFS::FileSystem::OperationNotAllowedError)
            ui.error "#{format_path.call(error.entry)} #{error.reason}." if ui
          else
            raise error
          end
        end
      end
      if !found_match
        ui.error "#{pattern}: No such file or directory on remote or local" if ui
        error = true
      end
      error
    end