├── .gitignore ├── README.textile ├── rsyncdiff.gemspec └── bin └── rsyncdiff /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | This is a simple tool for running diffs against local and remote code. It uses rysnc so it can get all the creations, deletions and updates. 2 | 3 | Run it like this: 4 | 5 | rsyncdiff server:/u/apps/myapp/current/app app/ 6 | 7 | !http://dl.getdropbox.com/u/221414/github/rsync_diff.png! 8 | 9 | It'll use colordiff if it's installed. 10 | 11 | h3. Installation 12 | 13 | This gem is hosted on "gemcutter":http://gemcutter.org 14 | 15 | Install with: 16 | 17 | gem install rsyncdiff 18 | -------------------------------------------------------------------------------- /rsyncdiff.gemspec: -------------------------------------------------------------------------------- 1 | Gem::Specification.new do |s| 2 | s.name = %q{rsyncdiff} 3 | s.version = "0.0.3" 4 | 5 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 6 | s.authors = ["Alex R. Young"] 7 | s.date = %q{2009-09-16} 8 | s.description = %q{rsyncdiff is a tool for comparing local and remote code. It displays changes, deletions and diffs.} 9 | s.email = %q{alex@alexyoung.org} 10 | s.files = %w{README.textile bin/rsyncdiff} 11 | s.has_rdoc = false 12 | s.bindir = 'bin' 13 | s.executables = ['rsyncdiff'] 14 | s.default_executable = 'bin/rsyncdiff' 15 | s.homepage = %q{http://github.com/alexyoung/rsyncdiff} 16 | s.summary = %q{rsyncdiff is a tool for comparing local and remote code.} 17 | end 18 | 19 | -------------------------------------------------------------------------------- /bin/rsyncdiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This script uses rsync to compare remote and local files. 4 | # It will display new files that would have been uploaded 5 | # and displays a diff for ones that have changes. 6 | 7 | begin 8 | remote_path = ARGV[0] 9 | path = ARGV[1] 10 | host, remote_path = remote_path.split(':') 11 | path = path + '/' if path[-1].chr != '/' 12 | rescue 13 | puts "Provide two arguments: [remote path] [local path]\n\n" 14 | puts "For example:" 15 | puts "\trsyncdiff server:/u/apps/myapp/current/app app/" 16 | exit 1 17 | end 18 | 19 | def colorize(text, color_code) 20 | "#{color_code}#{text}#{27.chr}[0m" 21 | end 22 | 23 | def red(text); colorize(text, "#{27.chr}[31m"); end 24 | def green(text); colorize(text, "#{27.chr}[32m"); end 25 | 26 | def use_colordiff? 27 | not `which colordiff`.empty? 28 | end 29 | 30 | class RemoteChange 31 | attr_reader :file 32 | 33 | def initialize(string, file) 34 | @string = string 35 | @file = file 36 | end 37 | 38 | def to_s 39 | change_type = if new? 40 | "[#{green 'CREATE'}]" 41 | elsif include?('new') 42 | "[#{green 'CREATE'}] #{output.first}" 43 | elsif change? 44 | "[#{green 'UPDATE'}] #{output[1]}" 45 | elsif delete? 46 | "[#{red 'DELETE'}] #{output[1]}" 47 | end 48 | 49 | "#{change_type}\t#{@file}" 50 | end 51 | 52 | def new? 53 | include?('uploaded') and include?('new') 54 | end 55 | 56 | def change? 57 | include?('uploaded') and include?('size') 58 | end 59 | 60 | def delete? 61 | include? 'deleted' 62 | end 63 | 64 | def output 65 | @results ||= parse 66 | end 67 | 68 | def include?(item) 69 | output.include? item 70 | end 71 | 72 | def interesting_deletion? 73 | @string.match /^\*deleting/ and !@string.match /\.svn/ 74 | end 75 | 76 | def parse 77 | return @results unless @results.nil? 78 | @string ||= '' 79 | 80 | @results = if interesting_deletion? 81 | ['deleted'] 82 | else 83 | @string.split('').collect do |c| 84 | identify c 85 | end 86 | end 87 | @results.compact 88 | end 89 | 90 | def identify(c) 91 | case c 92 | when '<' 93 | 'uploaded' 94 | when '>' 95 | 'downloaded' 96 | when 'c' 97 | 'local change/creation' 98 | when 'h' 99 | 'hard link' 100 | when '*' 101 | when 'f' 102 | 'file' 103 | when 'L' 104 | 'symlink' 105 | when '+' 106 | 'new' 107 | when '.' 108 | 'no change' 109 | when 's' 110 | 'size' 111 | end 112 | end 113 | end 114 | 115 | rsync_command = "rsync -rvv --itemize-changes --delete --dry-run #{path} #{host}:#{remote_path}" 116 | rsync_output = %x(#{rsync_command}) 117 | 118 | items = {} 119 | items[:creations] = [] 120 | items[:updates] = [] 121 | items[:deletions] = [] 122 | 123 | rsync_output.split("\n").each do |line| 124 | changes, file = line.gsub(/[ ]+/, ' ').split(' ') 125 | 126 | # ignore dot files 127 | next if file.nil? or file[0].chr == '.' or file.match('/\.') 128 | 129 | item = RemoteChange.new changes, file 130 | 131 | if item.change? 132 | items[:updates] << item 133 | elsif item.new? 134 | items[:creations] << item 135 | elsif item.delete? 136 | items[:deletions] << item 137 | end 138 | end 139 | 140 | items[:deletions].each do |item| 141 | puts item 142 | end 143 | 144 | puts 145 | 146 | items[:creations].each do |item| 147 | puts item 148 | end 149 | 150 | puts 151 | 152 | items[:updates].each do |item| 153 | puts item 154 | ssh_diff_command = "ssh #{host} cat \"#{File.join(remote_path, item.file)}\" | diff \"#{path}#{item.file}\" -" 155 | 156 | if use_colordiff? 157 | ssh_diff_command += ' | colordiff' 158 | end 159 | 160 | system(ssh_diff_command) 161 | puts 162 | end 163 | --------------------------------------------------------------------------------