sub 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +4 -0
 - data/License.txt +20 -0
 - data/Manifest.txt +10 -0
 - data/README.txt +1 -0
 - data/Rakefile +6 -0
 - data/bin/sub +3 -0
 - data/lib/sub.rb +3 -0
 - data/lib/sub/app.rb +356 -0
 - data/lib/sub/version.rb +9 -0
 - data/tasks/environment.rake +7 -0
 - metadata +59 -0
 
    
        data/History.txt
    ADDED
    
    
    
        data/License.txt
    ADDED
    
    | 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Copyright (c) 2007 Alex Chaffee
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining
         
     | 
| 
      
 4 
     | 
    
         
            +
            a copy of this software and associated documentation files (the
         
     | 
| 
      
 5 
     | 
    
         
            +
            "Software"), to deal in the Software without restriction, including
         
     | 
| 
      
 6 
     | 
    
         
            +
            without limitation the rights to use, copy, modify, merge, publish,
         
     | 
| 
      
 7 
     | 
    
         
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         
     | 
| 
      
 8 
     | 
    
         
            +
            permit persons to whom the Software is furnished to do so, subject to
         
     | 
| 
      
 9 
     | 
    
         
            +
            the following conditions:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be
         
     | 
| 
      
 12 
     | 
    
         
            +
            included in all copies or substantial portions of the Software.
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         
     | 
| 
      
 15 
     | 
    
         
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         
     | 
| 
      
 16 
     | 
    
         
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         
     | 
| 
      
 17 
     | 
    
         
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         
     | 
| 
      
 18 
     | 
    
         
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         
     | 
| 
      
 19 
     | 
    
         
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         
     | 
| 
      
 20 
     | 
    
         
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         
     | 
    
        data/Manifest.txt
    ADDED
    
    
    
        data/README.txt
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            README
         
     | 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/sub
    ADDED
    
    
    
        data/lib/sub.rb
    ADDED
    
    
    
        data/lib/sub/app.rb
    ADDED
    
    | 
         @@ -0,0 +1,356 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/local/bin/ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'benchmark'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            QUIET = 0
         
     | 
| 
      
 6 
     | 
    
         
            +
            NORMAL = 1
         
     | 
| 
      
 7 
     | 
    
         
            +
            VERBOSE = 2
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            class External
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
              def self.externals_in_directory(parent)
         
     | 
| 
      
 12 
     | 
    
         
            +
                exts = []
         
     | 
| 
      
 13 
     | 
    
         
            +
                prop = `svn propget svn:externals #{parent}`
         
     | 
| 
      
 14 
     | 
    
         
            +
                prop.split("\n").each do |prop_line|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  next if prop_line.strip.empty?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  exts << External.new(parent, prop_line)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
                exts
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
              attr_reader :name, :path, :url, :revision
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              def initialize(parent, property)
         
     | 
| 
      
 24 
     | 
    
         
            +
                match =  /^(\S+)\s+(-r\s*\d*\s+)?(\S+)\s*$/.match(property)
         
     | 
| 
      
 25 
     | 
    
         
            +
                @name = match[1]
         
     | 
| 
      
 26 
     | 
    
         
            +
                @revision = match[2] ? match[2].gsub(/\D/,'').to_i : nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                @url = match[3]
         
     | 
| 
      
 28 
     | 
    
         
            +
                @path = "#{parent}/#{@name}"
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def ==(other)
         
     | 
| 
      
 32 
     | 
    
         
            +
                (path == other.path and url == other.url)
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def to_s
         
     | 
| 
      
 36 
     | 
    
         
            +
                "External[name=#{name}, path=#{path}, url=#{url}, revision=#{revision}]"
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def revision_actual
         
     | 
| 
      
 40 
     | 
    
         
            +
                info = `svn info #{@path}`
         
     | 
| 
      
 41 
     | 
    
         
            +
                info.grep(/^Revision:/).first.gsub(/Revision: /, '').to_i
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              def should_update?
         
     | 
| 
      
 45 
     | 
    
         
            +
                revision == nil || revision_actual != revision
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            class Status
         
     | 
| 
      
 51 
     | 
    
         
            +
              class Line
         
     | 
| 
      
 52 
     | 
    
         
            +
                def initialize(text)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  text.strip!
         
     | 
| 
      
 54 
     | 
    
         
            +
                  @modified = /^M/.match(text) ? true : false
         
     | 
| 
      
 55 
     | 
    
         
            +
                  @external = /^X/.match(text) ? true : false
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @unversioned = /^\?/.match(text) ? true : false
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @modified_properties = /^.M/.match(text) ? true : false
         
     | 
| 
      
 58 
     | 
    
         
            +
                  @path = text.gsub(/^...... /, '')
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                attr_reader :path
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                def modified?
         
     | 
| 
      
 64 
     | 
    
         
            +
                  @modified
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
                def external?
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @external
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
                def unversioned?
         
     | 
| 
      
 70 
     | 
    
         
            +
                  @unversioned
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
                def modified_properties?
         
     | 
| 
      
 73 
     | 
    
         
            +
                  @modified_properties
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
              end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
              def initialize(text)
         
     | 
| 
      
 79 
     | 
    
         
            +
                @lines = text.split("\n").collect do |line|
         
     | 
| 
      
 80 
     | 
    
         
            +
                  Status::Line.new(line)
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
              def modified
         
     | 
| 
      
 85 
     | 
    
         
            +
                @lines.select { |line| line.modified? }.collect {|line| line.path}
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
              def externals
         
     | 
| 
      
 89 
     | 
    
         
            +
                @lines.select { |line| line.external? }.collect {|line| line.path}
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
              def unversioned
         
     | 
| 
      
 93 
     | 
    
         
            +
                @lines.select { |line| line.unversioned? }.collect {|line| line.path}
         
     | 
| 
      
 94 
     | 
    
         
            +
              end
         
     | 
| 
      
 95 
     | 
    
         
            +
            end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
            class Sub
         
     | 
| 
      
 98 
     | 
    
         
            +
              DEFAULT_BASE_URL = "svn+ssh://rubyforge.org/var/svn"
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              def self.from_args(args)
         
     | 
| 
      
 101 
     | 
    
         
            +
                verbosity = NORMAL
         
     | 
| 
      
 102 
     | 
    
         
            +
                still_parsing_options = true
         
     | 
| 
      
 103 
     | 
    
         
            +
                command = :up
         
     | 
| 
      
 104 
     | 
    
         
            +
                options = {}
         
     | 
| 
      
 105 
     | 
    
         
            +
                while still_parsing_options
         
     | 
| 
      
 106 
     | 
    
         
            +
                  case args[0]
         
     | 
| 
      
 107 
     | 
    
         
            +
                  when 'up', 'co', 'help'
         
     | 
| 
      
 108 
     | 
    
         
            +
                    options[:command] = args[0]
         
     | 
| 
      
 109 
     | 
    
         
            +
                  when '-v', '--verbose'
         
     | 
| 
      
 110 
     | 
    
         
            +
                    options[:verbosity] = VERBOSE
         
     | 
| 
      
 111 
     | 
    
         
            +
                  when '-q', '--quiet'
         
     | 
| 
      
 112 
     | 
    
         
            +
                    options[:verbosity] = QUIET
         
     | 
| 
      
 113 
     | 
    
         
            +
                  when '-h', '--help'
         
     | 
| 
      
 114 
     | 
    
         
            +
                    options[:command] = 'help'
         
     | 
| 
      
 115 
     | 
    
         
            +
                    still_parsing_options = false
         
     | 
| 
      
 116 
     | 
    
         
            +
                  when '-c', '--clean'
         
     | 
| 
      
 117 
     | 
    
         
            +
                    options[:clean] = true
         
     | 
| 
      
 118 
     | 
    
         
            +
                  when '--url'
         
     | 
| 
      
 119 
     | 
    
         
            +
                    args.shift
         
     | 
| 
      
 120 
     | 
    
         
            +
                    options[:url] = args.shift
         
     | 
| 
      
 121 
     | 
    
         
            +
                  else
         
     | 
| 
      
 122 
     | 
    
         
            +
                    still_parsing_options = false
         
     | 
| 
      
 123 
     | 
    
         
            +
                  end
         
     | 
| 
      
 124 
     | 
    
         
            +
                  args.shift if still_parsing_options
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                Sub.new(options, args)
         
     | 
| 
      
 128 
     | 
    
         
            +
              end
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              attr_reader :verbosity, :clean, :command, :args, :url
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
              def initialize(options = {:verbosity => NORMAL}, args = [])
         
     | 
| 
      
 133 
     | 
    
         
            +
                options = defaults.merge(options)
         
     | 
| 
      
 134 
     | 
    
         
            +
                @verbosity = options[:verbosity]
         
     | 
| 
      
 135 
     | 
    
         
            +
                @clean = options[:clean]
         
     | 
| 
      
 136 
     | 
    
         
            +
                @command = options[:command]
         
     | 
| 
      
 137 
     | 
    
         
            +
                @url = options[:url] || ENV['SUB_BASE_URL'] || DEFAULT_BASE_URL
         
     | 
| 
      
 138 
     | 
    
         
            +
                @args = args
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                @status = {}
         
     | 
| 
      
 141 
     | 
    
         
            +
              end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
              def defaults
         
     | 
| 
      
 144 
     | 
    
         
            +
                {
         
     | 
| 
      
 145 
     | 
    
         
            +
                  :verbosity => NORMAL,
         
     | 
| 
      
 146 
     | 
    
         
            +
                  :command => :up,
         
     | 
| 
      
 147 
     | 
    
         
            +
                }
         
     | 
| 
      
 148 
     | 
    
         
            +
              end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
              def execute
         
     | 
| 
      
 151 
     | 
    
         
            +
                self.send(@command)
         
     | 
| 
      
 152 
     | 
    
         
            +
              end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
              # commands
         
     | 
| 
      
 155 
     | 
    
         
            +
              def up
         
     | 
| 
      
 156 
     | 
    
         
            +
                if @args.empty?
         
     | 
| 
      
 157 
     | 
    
         
            +
                  @args = [`pwd`.chomp]
         
     | 
| 
      
 158 
     | 
    
         
            +
                end
         
     | 
| 
      
 159 
     | 
    
         
            +
                update_many(@args)
         
     | 
| 
      
 160 
     | 
    
         
            +
              end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
              def co
         
     | 
| 
      
 163 
     | 
    
         
            +
                if @args.empty?
         
     | 
| 
      
 164 
     | 
    
         
            +
                  raise "Please specify a project to check out"
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                project = args.shift
         
     | 
| 
      
 168 
     | 
    
         
            +
                dir_name = args.shift || project
         
     | 
| 
      
 169 
     | 
    
         
            +
                svn("co #{url}/#{project}/trunk #{dir_name}")
         
     | 
| 
      
 170 
     | 
    
         
            +
              end
         
     | 
| 
      
 171 
     | 
    
         
            +
             
     | 
| 
      
 172 
     | 
    
         
            +
              def help
         
     | 
| 
      
 173 
     | 
    
         
            +
                puts """
         
     | 
| 
      
 174 
     | 
    
         
            +
            sub - Alex's wrapper for subversion
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
            Usage:
         
     | 
| 
      
 177 
     | 
    
         
            +
              sub co project_name [dir_name]
         
     | 
| 
      
 178 
     | 
    
         
            +
                checks out [base_url]/project_name/trunk into ./project_name (or dir_name if specified)
         
     | 
| 
      
 179 
     | 
    
         
            +
              sub up [dir]*
         
     | 
| 
      
 180 
     | 
    
         
            +
                fast update (default command, so 'sub dir...' or just 'sub' work too)
         
     | 
| 
      
 181 
     | 
    
         
            +
              sub help
         
     | 
| 
      
 182 
     | 
    
         
            +
                prints this message
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
            Options:
         
     | 
| 
      
 185 
     | 
    
         
            +
              --verbose, -v
         
     | 
| 
      
 186 
     | 
    
         
            +
                  lots of output
         
     | 
| 
      
 187 
     | 
    
         
            +
              --quiet, -q
         
     | 
| 
      
 188 
     | 
    
         
            +
                  no output at all except for errors
         
     | 
| 
      
 189 
     | 
    
         
            +
              --help, -h
         
     | 
| 
      
 190 
     | 
    
         
            +
                  prints this message
         
     | 
| 
      
 191 
     | 
    
         
            +
              --clean, -c
         
     | 
| 
      
 192 
     | 
    
         
            +
                  'up' command removes all unversioned files and directories
         
     | 
| 
      
 193 
     | 
    
         
            +
              --url [base_url]
         
     | 
| 
      
 194 
     | 
    
         
            +
                  sets base repository url for 'co' command
         
     | 
| 
      
 195 
     | 
    
         
            +
                  (default is ENV['SUB_BASE_URL'] or #{DEFAULT_BASE_URL})
         
     | 
| 
      
 196 
     | 
    
         
            +
              """
         
     | 
| 
      
 197 
     | 
    
         
            +
              end
         
     | 
| 
      
 198 
     | 
    
         
            +
             
     | 
| 
      
 199 
     | 
    
         
            +
              # methods
         
     | 
| 
      
 200 
     | 
    
         
            +
             
     | 
| 
      
 201 
     | 
    
         
            +
              def update_many(roots)
         
     | 
| 
      
 202 
     | 
    
         
            +
                roots.each do |root|
         
     | 
| 
      
 203 
     | 
    
         
            +
                  say "Updating #{root}"
         
     | 
| 
      
 204 
     | 
    
         
            +
                  b = Benchmark.measure { update(root) }
         
     | 
| 
      
 205 
     | 
    
         
            +
                  say "Updated %s in %.2f sec" % [root, b.real]
         
     | 
| 
      
 206 
     | 
    
         
            +
                end
         
     | 
| 
      
 207 
     | 
    
         
            +
              end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
              def update(root)
         
     | 
| 
      
 210 
     | 
    
         
            +
                if @clean
         
     | 
| 
      
 211 
     | 
    
         
            +
                  remove_unversioned(root)
         
     | 
| 
      
 212 
     | 
    
         
            +
                end
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                externals_before = externals(root)
         
     | 
| 
      
 215 
     | 
    
         
            +
                svn("up --ignore-externals #{root}")
         
     | 
| 
      
 216 
     | 
    
         
            +
                externals = externals(root)
         
     | 
| 
      
 217 
     | 
    
         
            +
                # for some reason (array - array) doesn't work right here
         
     | 
| 
      
 218 
     | 
    
         
            +
                # so i had to write my own subtract method
         
     | 
| 
      
 219 
     | 
    
         
            +
                removed_externals = externals_before.subtract(externals)
         
     | 
| 
      
 220 
     | 
    
         
            +
                removed_externals.each do |ext|
         
     | 
| 
      
 221 
     | 
    
         
            +
                  say "Removed external #{ext}"
         
     | 
| 
      
 222 
     | 
    
         
            +
                  FileUtils.rm_rf(ext.path)
         
     | 
| 
      
 223 
     | 
    
         
            +
                end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                added_externals = externals.subtract(externals_before)
         
     | 
| 
      
 226 
     | 
    
         
            +
                existing_externals = externals.subtract(added_externals)
         
     | 
| 
      
 227 
     | 
    
         
            +
             
     | 
| 
      
 228 
     | 
    
         
            +
                # todo: extract Processes
         
     | 
| 
      
 229 
     | 
    
         
            +
                processes = []
         
     | 
| 
      
 230 
     | 
    
         
            +
                def update_external(processes, external)
         
     | 
| 
      
 231 
     | 
    
         
            +
                  pid = fork do
         
     | 
| 
      
 232 
     | 
    
         
            +
                    say "Updating external #{external.path}"
         
     | 
| 
      
 233 
     | 
    
         
            +
                    run("svn cleanup #{external.path}") if File.exists?(external.path)
         
     | 
| 
      
 234 
     | 
    
         
            +
                    rev = external.revision.nil? ? '' : "-r#{external.revision}"
         
     | 
| 
      
 235 
     | 
    
         
            +
                    svn("up #{rev} #{external.path} | grep -v 'At revision'")
         
     | 
| 
      
 236 
     | 
    
         
            +
                  end
         
     | 
| 
      
 237 
     | 
    
         
            +
                  processes << {:pid => pid, :external => external}
         
     | 
| 
      
 238 
     | 
    
         
            +
                end
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
                def checkout_external(processes, external)
         
     | 
| 
      
 241 
     | 
    
         
            +
                  pid = fork do
         
     | 
| 
      
 242 
     | 
    
         
            +
                    say "Checking out external #{external.path}"
         
     | 
| 
      
 243 
     | 
    
         
            +
                    rev = external.revision.nil? ? '' : "-r#{external.revision}"
         
     | 
| 
      
 244 
     | 
    
         
            +
                    svn("co #{rev} #{external.url} #{external.path}")
         
     | 
| 
      
 245 
     | 
    
         
            +
                  end
         
     | 
| 
      
 246 
     | 
    
         
            +
                  processes << {:pid => pid, :external => external}
         
     | 
| 
      
 247 
     | 
    
         
            +
                end
         
     | 
| 
      
 248 
     | 
    
         
            +
             
     | 
| 
      
 249 
     | 
    
         
            +
                added_externals.each do |external|
         
     | 
| 
      
 250 
     | 
    
         
            +
                  checkout_external(processes, external)
         
     | 
| 
      
 251 
     | 
    
         
            +
                end
         
     | 
| 
      
 252 
     | 
    
         
            +
             
     | 
| 
      
 253 
     | 
    
         
            +
                already_up_to_date = []
         
     | 
| 
      
 254 
     | 
    
         
            +
                existing_externals.each do |external|
         
     | 
| 
      
 255 
     | 
    
         
            +
                  if external.should_update?
         
     | 
| 
      
 256 
     | 
    
         
            +
                    update_external(processes, external)
         
     | 
| 
      
 257 
     | 
    
         
            +
                  else
         
     | 
| 
      
 258 
     | 
    
         
            +
                    already_up_to_date << external
         
     | 
| 
      
 259 
     | 
    
         
            +
                  end
         
     | 
| 
      
 260 
     | 
    
         
            +
                end
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
                unless already_up_to_date.empty?
         
     | 
| 
      
 263 
     | 
    
         
            +
                  say("External#{'s' if already_up_to_date.size > 1} " +
         
     | 
| 
      
 264 
     | 
    
         
            +
                    already_up_to_date.collect {|external| external.name}.join(", ") +
         
     | 
| 
      
 265 
     | 
    
         
            +
                    " already up to date")
         
     | 
| 
      
 266 
     | 
    
         
            +
                end
         
     | 
| 
      
 267 
     | 
    
         
            +
             
     | 
| 
      
 268 
     | 
    
         
            +
                processes.each do |process|
         
     | 
| 
      
 269 
     | 
    
         
            +
                  Process.waitpid(process[:pid], 0)
         
     | 
| 
      
 270 
     | 
    
         
            +
                end
         
     | 
| 
      
 271 
     | 
    
         
            +
              end
         
     | 
| 
      
 272 
     | 
    
         
            +
             
     | 
| 
      
 273 
     | 
    
         
            +
              def remove_unversioned(root)
         
     | 
| 
      
 274 
     | 
    
         
            +
                status(root).unversioned.each do |path|
         
     | 
| 
      
 275 
     | 
    
         
            +
                  if File.directory?(path)
         
     | 
| 
      
 276 
     | 
    
         
            +
                    say "Removing unversioned directory #{path}"
         
     | 
| 
      
 277 
     | 
    
         
            +
                  else
         
     | 
| 
      
 278 
     | 
    
         
            +
                    say "Removing unversioned file #{path}"
         
     | 
| 
      
 279 
     | 
    
         
            +
                  end
         
     | 
| 
      
 280 
     | 
    
         
            +
                  FileUtils.rm_rf(path)
         
     | 
| 
      
 281 
     | 
    
         
            +
                end
         
     | 
| 
      
 282 
     | 
    
         
            +
              end
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
              def status(root)
         
     | 
| 
      
 285 
     | 
    
         
            +
                @status[root] ||= Status.new(run("svn st #{root}", true))
         
     | 
| 
      
 286 
     | 
    
         
            +
              end
         
     | 
| 
      
 287 
     | 
    
         
            +
             
     | 
| 
      
 288 
     | 
    
         
            +
              def externals(root)
         
     | 
| 
      
 289 
     | 
    
         
            +
                exts = []
         
     | 
| 
      
 290 
     | 
    
         
            +
                directories_containing_externals(root).collect do |parent|
         
     | 
| 
      
 291 
     | 
    
         
            +
                  exts += External.externals_in_directory(parent)
         
     | 
| 
      
 292 
     | 
    
         
            +
                end
         
     | 
| 
      
 293 
     | 
    
         
            +
                exts
         
     | 
| 
      
 294 
     | 
    
         
            +
              end
         
     | 
| 
      
 295 
     | 
    
         
            +
             
     | 
| 
      
 296 
     | 
    
         
            +
              def directories_containing_externals(root)
         
     | 
| 
      
 297 
     | 
    
         
            +
                status(root).externals.collect do |path|
         
     | 
| 
      
 298 
     | 
    
         
            +
                  if (path !~ /\//)
         
     | 
| 
      
 299 
     | 
    
         
            +
                    "."
         
     | 
| 
      
 300 
     | 
    
         
            +
                  else
         
     | 
| 
      
 301 
     | 
    
         
            +
                    path.gsub(/\/[^\/]*$/, '')        
         
     | 
| 
      
 302 
     | 
    
         
            +
                  end
         
     | 
| 
      
 303 
     | 
    
         
            +
                end.uniq
         
     | 
| 
      
 304 
     | 
    
         
            +
              end
         
     | 
| 
      
 305 
     | 
    
         
            +
             
     | 
| 
      
 306 
     | 
    
         
            +
              def parse_externals(st)
         
     | 
| 
      
 307 
     | 
    
         
            +
                exts = []
         
     | 
| 
      
 308 
     | 
    
         
            +
                st.split("\n").select do |line|
         
     | 
| 
      
 309 
     | 
    
         
            +
                  line =~ /^X/
         
     | 
| 
      
 310 
     | 
    
         
            +
                end.collect do |line|
         
     | 
| 
      
 311 
     | 
    
         
            +
                  line.gsub(/^X */, '').gsub(/\/[^\/]*$/, '')
         
     | 
| 
      
 312 
     | 
    
         
            +
                end.uniq.collect do |parent|
         
     | 
| 
      
 313 
     | 
    
         
            +
                  prop = `svn propget svn:externals #{parent}`
         
     | 
| 
      
 314 
     | 
    
         
            +
                  prop.split("\n").each do |external|
         
     | 
| 
      
 315 
     | 
    
         
            +
                    next if external.strip.empty?
         
     | 
| 
      
 316 
     | 
    
         
            +
                    exts << External.new(parent, external)
         
     | 
| 
      
 317 
     | 
    
         
            +
                  end
         
     | 
| 
      
 318 
     | 
    
         
            +
                end
         
     | 
| 
      
 319 
     | 
    
         
            +
                exts
         
     | 
| 
      
 320 
     | 
    
         
            +
              end
         
     | 
| 
      
 321 
     | 
    
         
            +
             
     | 
| 
      
 322 
     | 
    
         
            +
              def say(msg)
         
     | 
| 
      
 323 
     | 
    
         
            +
                puts msg if verbosity > QUIET
         
     | 
| 
      
 324 
     | 
    
         
            +
              end
         
     | 
| 
      
 325 
     | 
    
         
            +
             
     | 
| 
      
 326 
     | 
    
         
            +
              def svn(cmd)
         
     | 
| 
      
 327 
     | 
    
         
            +
                svncmd = "svn"
         
     | 
| 
      
 328 
     | 
    
         
            +
                svncmd += " --quiet" if (cmd =~ /^(up|co)/ && verbosity == QUIET)
         
     | 
| 
      
 329 
     | 
    
         
            +
                run("#{svncmd} #{cmd}")
         
     | 
| 
      
 330 
     | 
    
         
            +
              end
         
     | 
| 
      
 331 
     | 
    
         
            +
             
     | 
| 
      
 332 
     | 
    
         
            +
              def run(cmd, return_output = false)
         
     | 
| 
      
 333 
     | 
    
         
            +
                say("\t#{cmd}") if verbosity == VERBOSE
         
     | 
| 
      
 334 
     | 
    
         
            +
                if (return_output)
         
     | 
| 
      
 335 
     | 
    
         
            +
                  `#{cmd}`
         
     | 
| 
      
 336 
     | 
    
         
            +
                else
         
     | 
| 
      
 337 
     | 
    
         
            +
                  cmd += ">/dev/null"if verbosity == QUIET
         
     | 
| 
      
 338 
     | 
    
         
            +
                  system(cmd)
         
     | 
| 
      
 339 
     | 
    
         
            +
                end
         
     | 
| 
      
 340 
     | 
    
         
            +
              end
         
     | 
| 
      
 341 
     | 
    
         
            +
            end
         
     | 
| 
      
 342 
     | 
    
         
            +
             
     | 
| 
      
 343 
     | 
    
         
            +
            class Array
         
     | 
| 
      
 344 
     | 
    
         
            +
              def subtract(other)
         
     | 
| 
      
 345 
     | 
    
         
            +
                self.select do |item|
         
     | 
| 
      
 346 
     | 
    
         
            +
                  !other.has?(item)
         
     | 
| 
      
 347 
     | 
    
         
            +
                end
         
     | 
| 
      
 348 
     | 
    
         
            +
              end
         
     | 
| 
      
 349 
     | 
    
         
            +
             
     | 
| 
      
 350 
     | 
    
         
            +
              def has?(something)
         
     | 
| 
      
 351 
     | 
    
         
            +
                self.each do |item|
         
     | 
| 
      
 352 
     | 
    
         
            +
                  return true if item == something
         
     | 
| 
      
 353 
     | 
    
         
            +
                end
         
     | 
| 
      
 354 
     | 
    
         
            +
                false
         
     | 
| 
      
 355 
     | 
    
         
            +
              end
         
     | 
| 
      
 356 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/sub/version.rb
    ADDED
    
    
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,59 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification 
         
     | 
| 
      
 2 
     | 
    
         
            +
            rubygems_version: 0.9.2
         
     | 
| 
      
 3 
     | 
    
         
            +
            specification_version: 1
         
     | 
| 
      
 4 
     | 
    
         
            +
            name: sub
         
     | 
| 
      
 5 
     | 
    
         
            +
            version: !ruby/object:Gem::Version 
         
     | 
| 
      
 6 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
      
 7 
     | 
    
         
            +
            date: 2007-09-12 00:00:00 -07:00
         
     | 
| 
      
 8 
     | 
    
         
            +
            summary: sub is a wrapper for svn that adds faster/safer updates, easy trunk checkout, etc.
         
     | 
| 
      
 9 
     | 
    
         
            +
            require_paths: 
         
     | 
| 
      
 10 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 11 
     | 
    
         
            +
            email: alex@pivotallabs.com
         
     | 
| 
      
 12 
     | 
    
         
            +
            homepage: http://pivotalrb.rubyforge.org
         
     | 
| 
      
 13 
     | 
    
         
            +
            rubyforge_project: pivotalrb
         
     | 
| 
      
 14 
     | 
    
         
            +
            description: sub is a wrapper for svn that adds faster/safer updates, easy trunk checkout, etc.
         
     | 
| 
      
 15 
     | 
    
         
            +
            autorequire: 
         
     | 
| 
      
 16 
     | 
    
         
            +
            default_executable: sub
         
     | 
| 
      
 17 
     | 
    
         
            +
            bindir: bin
         
     | 
| 
      
 18 
     | 
    
         
            +
            has_rdoc: true
         
     | 
| 
      
 19 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Version::Requirement 
         
     | 
| 
      
 20 
     | 
    
         
            +
              requirements: 
         
     | 
| 
      
 21 
     | 
    
         
            +
              - - ">"
         
     | 
| 
      
 22 
     | 
    
         
            +
                - !ruby/object:Gem::Version 
         
     | 
| 
      
 23 
     | 
    
         
            +
                  version: 0.0.0
         
     | 
| 
      
 24 
     | 
    
         
            +
              version: 
         
     | 
| 
      
 25 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 26 
     | 
    
         
            +
            signing_key: 
         
     | 
| 
      
 27 
     | 
    
         
            +
            cert_chain: 
         
     | 
| 
      
 28 
     | 
    
         
            +
            post_install_message: 
         
     | 
| 
      
 29 
     | 
    
         
            +
            authors: 
         
     | 
| 
      
 30 
     | 
    
         
            +
            - Alex Chaffee
         
     | 
| 
      
 31 
     | 
    
         
            +
            files: 
         
     | 
| 
      
 32 
     | 
    
         
            +
            - History.txt
         
     | 
| 
      
 33 
     | 
    
         
            +
            - License.txt
         
     | 
| 
      
 34 
     | 
    
         
            +
            - Manifest.txt
         
     | 
| 
      
 35 
     | 
    
         
            +
            - README.txt
         
     | 
| 
      
 36 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 37 
     | 
    
         
            +
            - bin/sub
         
     | 
| 
      
 38 
     | 
    
         
            +
            - lib/sub.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            - lib/sub/app.rb
         
     | 
| 
      
 40 
     | 
    
         
            +
            - lib/sub/version.rb
         
     | 
| 
      
 41 
     | 
    
         
            +
            - tasks/environment.rake
         
     | 
| 
      
 42 
     | 
    
         
            +
            test_files: []
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
            rdoc_options: 
         
     | 
| 
      
 45 
     | 
    
         
            +
            - --main
         
     | 
| 
      
 46 
     | 
    
         
            +
            - README.txt
         
     | 
| 
      
 47 
     | 
    
         
            +
            extra_rdoc_files: 
         
     | 
| 
      
 48 
     | 
    
         
            +
            - History.txt
         
     | 
| 
      
 49 
     | 
    
         
            +
            - License.txt
         
     | 
| 
      
 50 
     | 
    
         
            +
            - Manifest.txt
         
     | 
| 
      
 51 
     | 
    
         
            +
            - README.txt
         
     | 
| 
      
 52 
     | 
    
         
            +
            executables: 
         
     | 
| 
      
 53 
     | 
    
         
            +
            - sub
         
     | 
| 
      
 54 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            dependencies: []
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     |