svn-command 0.1.1 → 0.2.1
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/Readme +43 -18
- data/lib/svn-command/subversion.rb +93 -20
- data/lib/svn-command/subversion_extensions.rb +38 -15
- data/lib/svn-command/svn_command.rb +336 -42
- data/test/subversion_test.rb +44 -0
- data/test/svn_command_test.rb +168 -2
- metadata +7 -3
    
        data/Readme
    CHANGED
    
    | @@ -5,10 +5,12 @@ | |
| 5 5 | 
             
            [<b>Project site</b>:]     http://rubyforge.org/projects/svn-command
         | 
| 6 6 | 
             
            [<b>Wiki</b>:]             http://wiki.qualitysmith.com/svn-command
         | 
| 7 7 | 
             
            [<b>Author</b>:]           Tyler Rick
         | 
| 8 | 
            +
            [<b>Copyright</b>:]        2007 QualitySmith, Inc.
         | 
| 9 | 
            +
            [<b>License</b>:]          {GNU General Public License}[http://www.gnu.org/copyleft/gpl.html]
         | 
| 8 10 |  | 
| 9 11 | 
             
            == Introduction
         | 
| 10 12 |  | 
| 11 | 
            -
            This is a replacement <tt>svn</tt> <b>command-line client</b> meant to be used instead of the standard +svn+ command. Actually, it's a  | 
| 13 | 
            +
            This is a replacement <tt>svn</tt> <b>command-line client</b> meant to be used instead of the standard +svn+ command. Actually, it's a _wrapper_, not a replacement, because it still uses <tt>/usr/bin/svn</tt> to do all the dirty work.
         | 
| 12 14 |  | 
| 13 15 | 
             
            == Installation
         | 
| 14 16 |  | 
| @@ -44,29 +46,33 @@ You'll know it's working by way of two signs: | |
| 44 46 | 
             
            == Features
         | 
| 45 47 |  | 
| 46 48 | 
             
            Changes to existing subcommands:
         | 
| 47 | 
            -
            * <tt>svn diff</tt> | 
| 48 | 
            -
            *  | 
| 49 | 
            +
            * <tt>svn diff</tt>
         | 
| 50 | 
            +
              * output is in _color_* (requires +colordiff+, see below)
         | 
| 51 | 
            +
              * <tt>svn diff</tt> includes the differences from your *externals* too (consistent with how <tt>svn status</tt> includes them) so that you don't forget to commit those changes too! (pass <tt>--ignore-externals</tt> if you _don't_ want a diff of externals)
         | 
| 49 52 | 
             
            * <tt>svn status</tt>
         | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
            * <tt>svn move</tt>  | 
| 53 | 
            +
              * filters out distracting, useless output about externals (don't worry -- it still shows which files were _modified_)
         | 
| 54 | 
            +
              * the flags (?, M, C, etc.) are in *color*!
         | 
| 55 | 
            +
            * <tt>svn move</tt> it will let you move multiple source files to a destination directory with a single command
         | 
| 53 56 |  | 
| 54 57 | 
             
            (* You can pass --no-color to disable colors for a single command...useful if you want to pipe the output to another command or something. Eventually maybe we could make this a per-user option via .svn-command?)
         | 
| 55 58 |  | 
| 56 59 | 
             
            New subcommands:
         | 
| 57 60 | 
             
            * <tt>svn each_unadded</tt> (+eu+, +unadded+) -- goes through each unadded (<tt>?</tt>) file reported by <tt>svn status</tt> and asks you what to do with them (add, delete, ignore).
         | 
| 58 61 | 
             
            * <tt>svn externals</tt> -- lists all externals
         | 
| 62 | 
            +
            * <tt>svn revisions</tt> -- lists all revisions with log messages and lets you browse through them interactively
         | 
| 59 63 | 
             
            * <tt>svn edit_externals</tt> (+ee+)
         | 
| 60 64 | 
             
            * <tt>svn externalize</tt>
         | 
| 61 65 | 
             
            * <tt>svn set_message</tt> / <tt>svn get_message</tt> / <tt>svn edit_message</tt> -- shortcuts for accessing <tt>--revprop svn:log</tt>
         | 
| 62 66 | 
             
            * <tt>svn ignore</tt> -- shortcut for accessing <tt>svn:ignore</tt> property
         | 
| 63 67 | 
             
            * <tt>svn view_commits</tt> -- gives you output from both <tt>svn log</tt> and from <tt>svn diff</tt> for the given changesets (useful for code reviews)
         | 
| 68 | 
            +
            * <tt>svn repository_root</tt> -- prints out the root repository URL of the working copy you are in
         | 
| 69 | 
            +
            * <tt>svn delete_svn</tt> -- causes the current directory (recursively) to no longer be a working copy
         | 
| 64 70 |  | 
| 65 71 | 
             
            (RDoc question: how do I make the identifiers like Subversion::SvnCommand#externalize into links??)
         | 
| 66 72 |  | 
| 67 | 
            -
             | 
| 73 | 
            +
            = Usage / Examples
         | 
| 68 74 |  | 
| 69 | 
            -
             | 
| 75 | 
            +
            == <tt>svn st</tt>
         | 
| 70 76 |  | 
| 71 77 | 
             
            _Without_ this gem installed (really long):
         | 
| 72 78 |  | 
| @@ -106,7 +112,7 @@ _Without_ this gem installed (really long): | |
| 106 112 | 
             
              A      gemables/subversion/bin/svn
         | 
| 107 113 | 
             
              M      applications/underlord/vendor/plugins/rails_smith/tasks/shared/base.rake
         | 
| 108 114 |  | 
| 109 | 
            -
             | 
| 115 | 
            +
            == <tt>svn each_unadded</tt>
         | 
| 110 116 |  | 
| 111 117 | 
             
            My personal favorite. This command is useful for keeping your working copies clean -- getting rid of all those accumulated temp files (or *ignoring* or *adding* them if they're something that _all_ users of this repository should be aware of).
         | 
| 112 118 |  | 
| @@ -130,9 +136,9 @@ It simply goes through each "unadded" file (each file reporting a status of <tt> | |
| 130 136 | 
             
              Are you pretty much *SURE* you want to 'rm -rf applications/underlord/vendor/plugins/exception_notification'? (y)es, (n)o > y
         | 
| 131 137 | 
             
              Deleting...
         | 
| 132 138 |  | 
| 133 | 
            -
            For *files*, it will show a preview of the _contents_ of that file (limited to the first  | 
| 139 | 
            +
            For *files*, it will show a preview of the _contents_ of that file (limited to the first 55 lines); for *directories*, it will show a _directory_ _listing_. By looking at the preview, you should hopefully be able to decide whether you want to _keep_ the file or _junk_ it.
         | 
| 134 140 |  | 
| 135 | 
            -
             | 
| 141 | 
            +
            ==<tt>svn externalize</tt> / <tt>externals</tt> / <tt>edit_externals</tt>
         | 
| 136 142 |  | 
| 137 143 | 
             
            Shortcut for creating an svn:external...
         | 
| 138 144 |  | 
| @@ -168,7 +174,7 @@ You can also pass a directory name to edit_externals to edit the svn:externals p | |
| 168 174 |  | 
| 169 175 | 
             
              > svn edit-externals vendor/plugins
         | 
| 170 176 |  | 
| 171 | 
            -
             | 
| 177 | 
            +
            ==<tt>svn get_message</tt> / <tt>set_message</tt> / <tt>edit_message</tt>
         | 
| 172 178 |  | 
| 173 179 | 
             
            <b>Pre-requisite for set_message/edit_message</b>: Your repository must have a <tt>pre-revprop-change</tt> hook file.
         | 
| 174 180 |  | 
| @@ -187,18 +193,29 @@ You can do this: | |
| 187 193 | 
             
            or just this:
         | 
| 188 194 | 
             
              svn edit_message
         | 
| 189 195 |  | 
| 190 | 
            -
             | 
| 196 | 
            +
            == <tt>svn move</tt>
         | 
| 191 197 |  | 
| 192 198 | 
             
            You can now do commands like this:
         | 
| 193 199 |  | 
| 194 200 | 
             
              svn mv file1 file2 dir
         | 
| 195 201 | 
             
              svn mv dir1/* dir
         | 
| 196 202 |  | 
| 197 | 
            -
            (The  | 
| 203 | 
            +
            (The _standard_ +svn+ command only accepts a _single_ source and a _single_ destination!)
         | 
| 198 204 |  | 
| 199 | 
            -
             | 
| 205 | 
            +
            == <tt>svn revisions</tt> (revisions browser)
         | 
| 200 206 |  | 
| 201 | 
            -
             | 
| 207 | 
            +
            Lets you interactively step through all revisions of a file/directory/repository.
         | 
| 208 | 
            +
             | 
| 209 | 
            +
             | 
| 210 | 
            +
            Note: You may have to svn update your working copy in order for svn log (and hence svn revisions) to be able to see the revisions you just committed.
         | 
| 211 | 
            +
             | 
| 212 | 
            +
             | 
| 213 | 
            +
            Use the <tt>--forwards</tt> flag if you want to <u>start at the oldest revision</u> and step forwards through time rather than starting at the latest revision and stepping backwards (the default).
         | 
| 214 | 
            +
             | 
| 215 | 
            +
             | 
| 216 | 
            +
            == <tt>svn commit</tt>
         | 
| 217 | 
            +
             | 
| 218 | 
            +
            === --skip-notification / --covert
         | 
| 202 219 |  | 
| 203 220 | 
             
            Added a --skip-notification / --covert option which (assuming you have your post-commit hook set up to do this), will suppress the sending out of a commit notification e-mail.
         | 
| 204 221 |  | 
| @@ -249,11 +266,11 @@ For this option to have any effect, you will need to set up your repository simi | |
| 249 266 |  | 
| 250 267 |  | 
| 251 268 |  | 
| 252 | 
            -
             | 
| 269 | 
            +
            ==Help
         | 
| 253 270 |  | 
| 254 271 | 
             
            You can, of course, get a lits of the custom commands that have been added by using <tt>svn help</tt>. They will be listed at the end.
         | 
| 255 272 |  | 
| 256 | 
            -
             | 
| 273 | 
            +
            ==Global options
         | 
| 257 274 |  | 
| 258 275 | 
             
            * --no-color (since color is on by default)
         | 
| 259 276 | 
             
            * --dry-run  (see what /usr/bin/svn command it _would_ have executed if you weren't just doing a dry run -- useful for debugging if nothing else)
         | 
| @@ -283,6 +300,14 @@ If you want command completion for the svn subcommands (and I don't blame you if | |
| 283 300 |  | 
| 284 301 | 
             
            It's really rudimentary right now and could be much improved, but at least it's a start.
         | 
| 285 302 |  | 
| 303 | 
            +
            ==Support for code reviews, commit notification, and continuous integration systems
         | 
| 304 | 
            +
             | 
| 305 | 
            +
            The <tt>svn revisions</tt> command lets you browse through recent changes to a project or directory and then, for each revision that you review, you can simply press R and it will mark that revision as reviewed.
         | 
| 306 | 
            +
             | 
| 307 | 
            +
            <tt>svn commit</tt> accepts two custom flags, <tt>--skip-notification / --covert</tt> (don't send commit notification) and <tt>--broken</tt> (tell the continuous integration system to expect failure).
         | 
| 308 | 
            +
             | 
| 309 | 
            +
            =Other
         | 
| 310 | 
            +
             | 
| 286 311 | 
             
            ==Known problems
         | 
| 287 312 |  | 
| 288 313 | 
             
            It doesn't support options that are given in this format:
         | 
| @@ -21,6 +21,12 @@ gem 'qualitysmith_extensions', '>=0.0.7' | |
| 21 21 | 
             
            gem 'qualitysmith_extensions'
         | 
| 22 22 | 
             
            require 'qualitysmith_extensions/module/attribute_accessors'
         | 
| 23 23 |  | 
| 24 | 
            +
            # RSCM is used for some of the abstraction, such as for parsing log messages into nice data structures. It seems like overkill, though, to use RSCM for most things...
         | 
| 25 | 
            +
            gem 'rscm'
         | 
| 26 | 
            +
            #require 'rscm'
         | 
| 27 | 
            +
            #require 'rscm/scm/subversion'
         | 
| 28 | 
            +
            require 'rscm/scm/subversion_log_parser'
         | 
| 29 | 
            +
             | 
| 24 30 | 
             
            # Wraps the Subversion shell commands for Ruby.
         | 
| 25 31 | 
             
            module Subversion
         | 
| 26 32 | 
             
              # True if you want output from svn to be colorized (useful if output is for human eyes, but not useful if using the output programatically)
         | 
| @@ -137,6 +143,9 @@ module Subversion | |
| 137 143 | 
             
                execute("update #{args.join ' '}")
         | 
| 138 144 | 
             
              end
         | 
| 139 145 |  | 
| 146 | 
            +
              # The output from `svn status` is nicely divided into two "sections": the section which pertains to the current working copy (not
         | 
| 147 | 
            +
              # counting externals as part of the working copy) and then the section with status of all of the externals.
         | 
| 148 | 
            +
              # This method returns the first section.
         | 
| 140 149 | 
             
              def self.status_the_section_before_externals(path = './')
         | 
| 141 150 | 
             
                status = status(path) || ''
         | 
| 142 151 | 
             
                status.sub!(/(Performing status.*)/m, '')
         | 
| @@ -197,15 +206,47 @@ module Subversion | |
| 197 206 | 
             
                self.set_property property, lines.join("\n"), path
         | 
| 198 207 | 
             
              end
         | 
| 199 208 |  | 
| 209 | 
            +
              # :todo: Stop assuming the svn: namespace. What's the point of a namespace if you only allow one of them?
         | 
| 200 210 | 
             
              def self.get_property(property, path = './')
         | 
| 201 211 | 
             
                execute "propget svn:#{property} #{path}"
         | 
| 202 212 | 
             
              end
         | 
| 213 | 
            +
              def self.get_revision_property(property_name, rev)
         | 
| 214 | 
            +
                execute("propget --revprop #{property_name} -r #{rev}").chomp
         | 
| 215 | 
            +
              end
         | 
| 216 | 
            +
             | 
| 203 217 | 
             
              def self.delete_property(property, path = './')
         | 
| 204 218 | 
             
                execute "propdel svn:#{property} #{path}"
         | 
| 205 219 | 
             
              end
         | 
| 220 | 
            +
              def self.delete_revision_property(property_name, rev)
         | 
| 221 | 
            +
                execute("propdel --revprop #{property_name} -r #{rev}").chomp
         | 
| 222 | 
            +
              end
         | 
| 223 | 
            +
             | 
| 206 224 | 
             
              def self.set_property(property, value, path = './')
         | 
| 207 225 | 
             
                execute "propset svn:#{property} '#{value}' #{path}"
         | 
| 208 226 | 
             
              end
         | 
| 227 | 
            +
              def self.set_revision_property(property_name, rev)
         | 
| 228 | 
            +
                execute("propset --revprop #{property_name} -r #{rev}").chomp
         | 
| 229 | 
            +
              end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
              # Gets raw output of proplist command
         | 
| 232 | 
            +
              def self.proplist(rev)
         | 
| 233 | 
            +
                execute("proplist --revprop -r #{rev}")
         | 
| 234 | 
            +
              end
         | 
| 235 | 
            +
              # Returns an array of the names of all revision properties currently set on the given +rev+
         | 
| 236 | 
            +
              # Tested by: ../../test/subversion_test.rb:test_revision_properties_names
         | 
| 237 | 
            +
              def self.revision_properties_names(rev)
         | 
| 238 | 
            +
                raw_list = proplist(rev)
         | 
| 239 | 
            +
                raw_list.scan(/^ +([^ ]+)$/).map { |matches|
         | 
| 240 | 
            +
                  matches.first.chomp
         | 
| 241 | 
            +
                }
         | 
| 242 | 
            +
              end
         | 
| 243 | 
            +
              # Returns an array of RevisionProperty objects (name, value) for revisions currently set on the given +rev+
         | 
| 244 | 
            +
              # Tested by: ../../test/subversion_test.rb:test_revision_properties
         | 
| 245 | 
            +
              def self.revision_properties(rev)
         | 
| 246 | 
            +
                revision_properties_names(rev).map { |property_name|
         | 
| 247 | 
            +
                  RevisionProperty.new(property_name, get_revision_property(property_name, rev))
         | 
| 248 | 
            +
                }
         | 
| 249 | 
            +
              end
         | 
| 209 250 |  | 
| 210 251 | 
             
              def self.make_directory(dir)
         | 
| 211 252 | 
             
                execute "mkdir #{dir}"
         | 
| @@ -215,16 +256,32 @@ module Subversion | |
| 215 256 | 
             
                execute "help #{args.join(' ')}"
         | 
| 216 257 | 
             
              end
         | 
| 217 258 |  | 
| 259 | 
            +
              # Returns the raw output from svn log
         | 
| 218 260 | 
             
              def self.log(*args)
         | 
| 219 261 | 
             
                args = ['./'] if args.empty?
         | 
| 220 262 | 
             
                execute "log #{args.join(' ')}"
         | 
| 221 263 | 
             
              end
         | 
| 264 | 
            +
              # Returns the revision number for head.
         | 
| 222 265 | 
             
              def self.latest_revision(*args)
         | 
| 223 266 | 
             
                args = ['./'] if args.empty?
         | 
| 224 267 | 
             
                matches = /Status against revision:\s+(\d+)/m.match(status_against_server(args))
         | 
| 225 268 | 
             
                matches && matches[1]
         | 
| 226 269 | 
             
              end
         | 
| 227 270 |  | 
| 271 | 
            +
              # Returns an array of RSCM::Revision objects
         | 
| 272 | 
            +
              def self.revisions(*args)
         | 
| 273 | 
            +
                # Tried using this, but it seems to expect you to pass in a starting date or accept the default starting date of right now, which is silly if you actually just want *all* revisions...
         | 
| 274 | 
            +
                #@rscm = ::RSCM::Subversion.new
         | 
| 275 | 
            +
                #@rscm.revisions
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                #log_output = Subversion.log('-v')
         | 
| 278 | 
            +
                log_output = Subversion.log(*(['-v'] + args))
         | 
| 279 | 
            +
                parser = ::RSCM::SubversionLogParser.new(io = StringIO.new(log_output), url = 'http://ignore.me.com')
         | 
| 280 | 
            +
                revisions = parser.parse_revisions
         | 
| 281 | 
            +
                revisions
         | 
| 282 | 
            +
              end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
             | 
| 228 285 | 
             
              def self.info(*args)
         | 
| 229 286 | 
             
                args = ['./'] if args.empty?
         | 
| 230 287 | 
             
                execute "info #{args.join(' ')}"
         | 
| @@ -232,26 +289,32 @@ module Subversion | |
| 232 289 |  | 
| 233 290 | 
             
              # :todo: needs some serious unit-testing love
         | 
| 234 291 | 
             
              def self.base_url(path_or_url = './')
         | 
| 235 | 
            -
             | 
| 236 | 
            -
             | 
| 237 | 
            -
             | 
| 238 | 
            -
             | 
| 239 | 
            -
             | 
| 240 | 
            -
             | 
| 241 | 
            -
             | 
| 242 | 
            -
             | 
| 243 | 
            -
                   | 
| 244 | 
            -
             | 
| 245 | 
            -
                   | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
             | 
| 250 | 
            -
             | 
| 251 | 
            -
             | 
| 252 | 
            -
                   | 
| 253 | 
            -
             | 
| 254 | 
            -
             | 
| 292 | 
            +
                 matches = info(path_or_url).match(/^Repository Root: (.+)/)
         | 
| 293 | 
            +
                 matches && matches[1]
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                 # It appears that we might need to use this old way (which looks at 'URL'), since there is actually a 
         | 
| 296 | 
            +
            #    base_url = nil    # needed so that base_url variable isn't local to loop block (and reset during next iteration)!
         | 
| 297 | 
            +
            #    started_using_dot_dots = false
         | 
| 298 | 
            +
            #    loop do
         | 
| 299 | 
            +
            #      matches = /^URL: (.+)/.match(info(path_or_url))
         | 
| 300 | 
            +
            #      if matches && matches[1]
         | 
| 301 | 
            +
            #        base_url = matches[1]
         | 
| 302 | 
            +
            #      else
         | 
| 303 | 
            +
            #        break base_url
         | 
| 304 | 
            +
            #      end
         | 
| 305 | 
            +
            #
         | 
| 306 | 
            +
            #      # Keep going up the path, one directory at a time, until `svn info` no longer returns a URL (will probably eventually return 'svn: PROPFIND request failed')
         | 
| 307 | 
            +
            #      if path_or_url.include?('/') && !started_using_dot_dots
         | 
| 308 | 
            +
            #        path_or_url = File.dirname(path_or_url)
         | 
| 309 | 
            +
            #      else
         | 
| 310 | 
            +
            #        started_using_dot_dots = true
         | 
| 311 | 
            +
            #        path_or_url = File.join(path_or_url, '..')
         | 
| 312 | 
            +
            #      end
         | 
| 313 | 
            +
            #      #puts 'going up to ' + path_or_url
         | 
| 314 | 
            +
            #    end
         | 
| 315 | 
            +
              end
         | 
| 316 | 
            +
              def self.root_url(*args);        base_url(*args); end
         | 
| 317 | 
            +
              def self.repository_root(*args); base_url(*args); end
         | 
| 255 318 |  | 
| 256 319 | 
             
              # The location of the executable to be used
         | 
| 257 320 | 
             
              def self.executable
         | 
| @@ -294,11 +357,17 @@ protected | |
| 294 357 |  | 
| 295 358 | 
             
                valid_options = [:capture, :exec, :popen]
         | 
| 296 359 | 
             
                case method
         | 
| 360 | 
            +
             | 
| 297 361 | 
             
                  when :capture
         | 
| 298 362 | 
             
                    `#{command} 2>&1` 
         | 
| 363 | 
            +
             | 
| 299 364 | 
             
                  when :exec
         | 
| 300 365 | 
             
                    #Kernel.exec *args
         | 
| 301 366 | 
             
                    Kernel.exec command
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                  when :system
         | 
| 369 | 
            +
                    Kernel.system command
         | 
| 370 | 
            +
             | 
| 302 371 | 
             
                  when :popen
         | 
| 303 372 | 
             
                    # This is just an idea of how maybe we could improve the LATENCY. Rather than waiting until the command completes
         | 
| 304 373 | 
             
                    # (which can take quite a while for svn status sometimes since it has to walk the entire directory tree), why not process
         | 
| @@ -338,8 +407,12 @@ end | |
| 338 407 |  | 
| 339 408 |  | 
| 340 409 |  | 
| 410 | 
            +
            #Subversion.const_set(:RevisionProperty) = Struct.new(:name, :repository_path)
         | 
| 341 411 |  | 
| 342 412 | 
             
            module Subversion
         | 
| 413 | 
            +
             | 
| 414 | 
            +
              RevisionProperty = Struct.new(:name, :value)
         | 
| 415 | 
            +
             | 
| 343 416 | 
             
              # Represents an "externals container", which is a directory that has the <tt>svn:externals</tt> property set to something useful.
         | 
| 344 417 | 
             
              # Each ExternalsContainer contains a set of "entries", which are the actual directories listed in the <tt>svn:externals</tt>
         | 
| 345 418 | 
             
              # property and are "pulled into" the directory.
         | 
| @@ -1,4 +1,4 @@ | |
| 1 | 
            -
            # Tested by:  | 
| 1 | 
            +
            # Tested by: ../../test/subversion_extensions_test.rb
         | 
| 2 2 |  | 
| 3 3 | 
             
            gem 'colored'
         | 
| 4 4 | 
             
            require 'colored'
         | 
| @@ -10,26 +10,42 @@ class Array | |
| 10 10 | 
             
            end
         | 
| 11 11 |  | 
| 12 12 | 
             
            class String
         | 
| 13 | 
            +
              def colorize_svn_question_mark; self.yellow.bold; end
         | 
| 14 | 
            +
              def colorize_svn_add;           self.green.bold; end
         | 
| 15 | 
            +
              def colorize_svn_modified;      self.cyan.bold; end
         | 
| 16 | 
            +
              def colorize_svn_updated;       self.yellow.bold; end
         | 
| 17 | 
            +
              def colorize_svn_deleted;       self.magenta.bold; end
         | 
| 18 | 
            +
              def colorize_svn_conflict;      self.red.bold; end
         | 
| 19 | 
            +
              def colorize_svn_tilde;         self.red.bold; end
         | 
| 20 | 
            +
              def colorize_svn_exclamation;   self.red.bold; end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              def colorize_svn_status_code
         | 
| 23 | 
            +
                if Subversion.color
         | 
| 24 | 
            +
                  self.gsub('?') { $&.colorize_svn_question_mark }.
         | 
| 25 | 
            +
                       gsub('A') { $&.colorize_svn_add }.
         | 
| 26 | 
            +
                       gsub('M') { $&.colorize_svn_modified }.
         | 
| 27 | 
            +
                       gsub('D') { $&.colorize_svn_deleted }.
         | 
| 28 | 
            +
                       gsub('C') { $&.colorize_svn_conflict }.
         | 
| 29 | 
            +
                       gsub('~') { $&.colorize_svn_tilde }.
         | 
| 30 | 
            +
                       gsub('!') { $&.colorize_svn_exclamation }
         | 
| 31 | 
            +
                else
         | 
| 32 | 
            +
                  self
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 13 35 | 
             
              def colorize_svn_status_lines
         | 
| 14 36 | 
             
                if Subversion.color
         | 
| 15 | 
            -
                  self.gsub(/^  | 
| 16 | 
            -
                       gsub(/^ *A/)  { $&.green.bold}.
         | 
| 17 | 
            -
                       gsub(/^ *M/)  { $&.green.bold}.
         | 
| 18 | 
            -
                       gsub(/^ *D/)  { $&.magenta.bold}.
         | 
| 19 | 
            -
                       gsub(/^ *C/)  { $&.red.bold}.
         | 
| 20 | 
            -
                       gsub(/^ *~/)  { $&.red.bold}.
         | 
| 21 | 
            -
                       gsub(/^ *!/)  { $&.red.bold}
         | 
| 37 | 
            +
                  self.gsub(/^ *([^ ])\s/) { $&.colorize_svn_status_code }
         | 
| 22 38 | 
             
                else
         | 
| 23 39 | 
             
                  self
         | 
| 24 40 | 
             
                end
         | 
| 25 41 | 
             
              end
         | 
| 26 42 | 
             
              def colorize_svn_update_lines
         | 
| 27 43 | 
             
                if Subversion.color
         | 
| 28 | 
            -
                  self.gsub(/^ *U\s/)  { $&. | 
| 29 | 
            -
                       gsub(/^ *A\s/)  { $&. | 
| 30 | 
            -
                       gsub(/^ *M\s/)  { $&. | 
| 31 | 
            -
                       gsub(/^ *D\s/)  { $&. | 
| 32 | 
            -
                       gsub(/^ *C\s/)  { $&. | 
| 44 | 
            +
                  self.gsub(/^ *U\s/)  { $&.colorize_svn_updated }.
         | 
| 45 | 
            +
                       gsub(/^ *A\s/)  { $&.colorize_svn_add }.
         | 
| 46 | 
            +
                       gsub(/^ *M\s/)  { $&.colorize_svn_modified }.
         | 
| 47 | 
            +
                       gsub(/^ *D\s/)  { $&.colorize_svn_deleted }.
         | 
| 48 | 
            +
                       gsub(/^ *C\s/)  { $&.colorize_svn_conflict }
         | 
| 33 49 | 
             
                else
         | 
| 34 50 | 
             
                  self
         | 
| 35 51 | 
             
                end
         | 
| @@ -110,9 +126,16 @@ module Subversion | |
| 110 126 | 
             
                  }
         | 
| 111 127 | 
             
                end
         | 
| 112 128 |  | 
| 113 | 
            -
                # This is just a wrapper for Subversion | 
| 129 | 
            +
                # This is just a wrapper for Subversion::diff that adds some color
         | 
| 114 130 | 
             
                def self.diff(*args)
         | 
| 115 | 
            -
                   | 
| 131 | 
            +
                  Subversion::diff(*args).colorize_svn_diff.add_exit_code_error
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                # A wrapper for Subversion::revision_properties that formats it for display on srceen
         | 
| 135 | 
            +
                def self.printable_revision_properties(rev)
         | 
| 136 | 
            +
                  Subversion::revision_properties(rev).map do |property|
         | 
| 137 | 
            +
                    "#{property.name.ljust(20)} = '#{property.value}'"
         | 
| 138 | 
            +
                  end.join("\n")
         | 
| 116 139 | 
             
                end
         | 
| 117 140 |  | 
| 118 141 | 
             
              end
         | 
| @@ -7,6 +7,7 @@ require 'facets/core/kernel/require_local' | |
| 7 7 | 
             
            require 'facets/core/array/select'      # select!
         | 
| 8 8 | 
             
            require 'facets/core/kernel/with'       # returning
         | 
| 9 9 | 
             
            require 'facets/core/string/lines'
         | 
| 10 | 
            +
            require 'facets/core/string/index_all'
         | 
| 10 11 |  | 
| 11 12 | 
             
            gem 'qualitysmith_extensions', '>=0.0.3'
         | 
| 12 13 | 
             
            require 'qualitysmith_extensions/enumerable/enum'
         | 
| @@ -22,6 +23,8 @@ require 'termios' | |
| 22 23 | 
             
            require 'stringio'
         | 
| 23 24 | 
             
            gem 'colored'
         | 
| 24 25 | 
             
            require 'colored'  # Lets us do "a".white.bold instead of "\033[1ma\033[0m"
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            require_local '../../ProjectInfo'
         | 
| 25 28 | 
             
            require_local 'subversion'
         | 
| 26 29 | 
             
            require_local 'subversion_extensions'
         | 
| 27 30 |  | 
| @@ -42,8 +45,14 @@ end | |
| 42 45 |  | 
| 43 46 | 
             
            class String
         | 
| 44 47 | 
             
              # Makes the first character bold and underlined. Makes the whole string of the given color.
         | 
| 45 | 
            -
               | 
| 46 | 
            -
             | 
| 48 | 
            +
              # :todo: Move out to extensions/console/menu_item
         | 
| 49 | 
            +
              def menu_item(color = :white, letter = self[0..0], which_occurence = 0)
         | 
| 50 | 
            +
                index = index_all(/#{letter}/)[which_occurence]
         | 
| 51 | 
            +
                raise "Could not find a #{which_occurence}th occurence of '#{letter}' in string '#{self}'" if index.nil?
         | 
| 52 | 
            +
                before = self[0..index-1].send(color) unless index == 0
         | 
| 53 | 
            +
                middle = self[index..index].send(color).bold.underline
         | 
| 54 | 
            +
                after  = self[index+1..-1].send(color)
         | 
| 55 | 
            +
                before.to_s + middle + after
         | 
| 47 56 | 
             
              end
         | 
| 48 57 | 
             
              def add_exit_code_error
         | 
| 49 58 | 
             
                self << "Exited with error!".bold.red if !$?.success?
         | 
| @@ -74,8 +83,12 @@ module Subversion | |
| 74 83 | 
             
                  'each_unadded',
         | 
| 75 84 | 
             
                  'externals_items', 'externals_outline', 'externals_containers', 'edit_externals', 'externalize',
         | 
| 76 85 | 
             
                  'ignore',
         | 
| 86 | 
            +
                  'revisions',
         | 
| 77 87 | 
             
                  'get_message', 'set_message', 'edit_message',
         | 
| 78 | 
            -
                  'view_commits'
         | 
| 88 | 
            +
                  'view_commits',
         | 
| 89 | 
            +
                  'repository_root',
         | 
| 90 | 
            +
                  'latest_revision',
         | 
| 91 | 
            +
                  'delete_svn'
         | 
| 79 92 | 
             
                ]
         | 
| 80 93 | 
             
                mattr_reader :subcommand_list
         | 
| 81 94 |  | 
| @@ -87,7 +100,7 @@ module Subversion | |
| 87 100 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 88 101 | 
             
                # Global options
         | 
| 89 102 |  | 
| 90 | 
            -
                global_option :__no_color, :__dry_run, :__debug
         | 
| 103 | 
            +
                global_option :__no_color, :__dry_run, :__debug, :__print_commands
         | 
| 91 104 | 
             
                def __no_color
         | 
| 92 105 | 
             
                  Subversion::color = false
         | 
| 93 106 | 
             
                end
         | 
| @@ -101,10 +114,20 @@ module Subversion | |
| 101 114 | 
             
                def __print_commands
         | 
| 102 115 | 
             
                  Subversion::print_commands = true
         | 
| 103 116 | 
             
                end
         | 
| 104 | 
            -
                # Don't want to hide svn's own -v/--verbose flag
         | 
| 105 | 
            -
                alias_method :__Verbose,        :__print_commands
         | 
| 106 117 | 
             
                alias_method :__show_commands,  :__print_commands
         | 
| 118 | 
            +
                # Don't want to hide/conflict with svn's own -v/--verbose flag, so using capital initial letter
         | 
| 107 119 | 
             
                alias_method :_V,               :__print_commands
         | 
| 120 | 
            +
                alias_method :__Verbose,        :__print_commands
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                # Usually most Subversion commands are recursive and all-inclusive. This option adds file *exclusion* to most of Subversion's commands.
         | 
| 123 | 
            +
                # Use this if you want to commit (/add/etc.) everything *but* a certain file or set of files
         | 
| 124 | 
            +
                #   svn commit dir1 dir2 --except dir1/not_ready_yet.rb
         | 
| 125 | 
            +
                def __except
         | 
| 126 | 
            +
                  # We'll have to use a FileList to do this. This option will remove all file arguments, put them into a FileList as inclusions, 
         | 
| 127 | 
            +
                  # add the exclusions, and then pass the resulting list of files on to the *actual* svn command.
         | 
| 128 | 
            +
                  # :todo:
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
                alias_method :__exclude, :__except
         | 
| 108 131 |  | 
| 109 132 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 110 133 | 
             
                # Default/dynamic behavior
         | 
| @@ -188,20 +211,44 @@ module Subversion | |
| 188 211 | 
             
                      [:__encoding] => 1,
         | 
| 189 212 | 
             
                    }.merge(SvnCommand::standard_remote_command_options), self
         | 
| 190 213 | 
             
                  )
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                  # Use this flag if you don't want a commit notification to be sent out.
         | 
| 191 216 | 
             
                  def __skip_notification
         | 
| 192 217 | 
             
                    @skip_notification = true
         | 
| 193 218 | 
             
                  end
         | 
| 194 219 | 
             
                  alias_method :__covert, :__skip_notification
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                  # Use this flag if you are about to commit some code for which you know the tests aren't or (probaby won't) pass.
         | 
| 222 | 
            +
                  # This *may* cause your continuous integration system to either skip tests for this revision or at least be a little more
         | 
| 223 | 
            +
                  # *leniant* towards you (a slap on the wrist instead of a public flogging, perhaps) when it runs the tests and finds that
         | 
| 224 | 
            +
                  # they *are* failing.
         | 
| 225 | 
            +
                  # You should probably only do this if you are planning on making multiple commits in rapid succession (sometimes Subversion
         | 
| 226 | 
            +
                  # forces you to do an intermediate commit in order to move something that's already been scheduled for a move or somethhing,
         | 
| 227 | 
            +
                  # for example). If things will be broken for a while, consider starting a branch for your changes and merging the branch
         | 
| 228 | 
            +
                  # back into trunk only when you've gotten the code stable/working again.
         | 
| 229 | 
            +
                  # (See http://svn.collab.net/repos/svn/trunk/doc/user/svn-best-practices.html)
         | 
| 230 | 
            +
                  def __broken
         | 
| 231 | 
            +
                    @broken = true
         | 
| 232 | 
            +
                  end
         | 
| 233 | 
            +
                  alias_method :__expect_to_break_tests,            :__broken
         | 
| 234 | 
            +
                  alias_method :__knowingly_committing_broken_code, :__broken
         | 
| 195 235 | 
             
                end
         | 
| 196 236 | 
             
                def commit(*args)
         | 
| 197 237 | 
             
                  Subversion.print_commands_for do
         | 
| 198 | 
            -
                    svn | 
| 238 | 
            +
                    puts svn(:capture, "propset svn:skip_commit_notification_for_next_commit true --revprop -r #{Subversion.latest_revision}", :prepare_args => false)
         | 
| 199 239 | 
             
                  end if @skip_notification
         | 
| 200 | 
            -
                  svn :exec, 'commit', *(['--force-log'] + args)
         | 
| 201 | 
            -
             | 
| 202 240 | 
             
                  # :todo:
         | 
| 203 241 | 
             
                  # Add some logic to automatically skip the commit e-mail if the size of the files to be committed exceeds a threshold of __ MB.
         | 
| 204 242 | 
             
                  # (Performance idea: Only check the size of the files if svn st includes (bin)?)
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                  puts output = svn(:capture, 'commit', *(['--force-log'] + args))
         | 
| 245 | 
            +
             | 
| 246 | 
            +
                  just_committed = (matches = output.match(/Committed revision (\d+)\./)) && matches[1]
         | 
| 247 | 
            +
             | 
| 248 | 
            +
                  Subversion.print_commands_for do
         | 
| 249 | 
            +
                    puts svn(:capture, "propset code:broken true --revprop -r #{just_committed}", :prepare_args => false)
         | 
| 250 | 
            +
                  end if @broken
         | 
| 251 | 
            +
             | 
| 205 252 | 
             
                end
         | 
| 206 253 |  | 
| 207 254 | 
             
                # Ideas:
         | 
| @@ -222,17 +269,20 @@ module Subversion | |
| 222 269 | 
             
                      [:__force] => 1,
         | 
| 223 270 | 
             
                    }.merge(SvnCommand::standard_remote_command_options), self
         | 
| 224 271 | 
             
                  )
         | 
| 272 | 
            +
                  def __ignore_externals
         | 
| 273 | 
            +
                    @ignore_externals = true
         | 
| 274 | 
            +
                  end
         | 
| 225 275 | 
             
                end
         | 
| 226 276 |  | 
| 227 277 | 
             
                def diff(*directories)
         | 
| 228 | 
            -
                  directories = [ | 
| 278 | 
            +
                  directories = ['./'] if directories.empty?
         | 
| 229 279 | 
             
                  puts Extensions.diff(*(directories + @passthrough_options))
         | 
| 230 280 |  | 
| 231 | 
            -
                  # Show diff for externals (if there are any)
         | 
| 232 | 
            -
             | 
| 233 | 
            -
             | 
| 234 | 
            -
             | 
| 235 | 
            -
             | 
| 281 | 
            +
                  begin # Show diff for externals (if there *are* any and the user didn't tell us to ignore them)
         | 
| 282 | 
            +
                    output = StringIO.new
         | 
| 283 | 
            +
                    #paths = args.reject{|arg| arg =~ /^-/} || ['./']
         | 
| 284 | 
            +
                    directories.each do |path|
         | 
| 285 | 
            +
                      (Subversion.externals_items(path) || []).each do |item|
         | 
| 236 286 | 
             
                        diff_output = Extensions.diff(item).strip
         | 
| 237 287 | 
             
                        unless diff_output == ""
         | 
| 238 288 | 
             
                          #output.puts '-'*100
         | 
| @@ -240,14 +290,15 @@ module Subversion | |
| 240 290 | 
             
                          output.puts item.black_on_white.bold
         | 
| 241 291 | 
             
                          output.puts diff_output
         | 
| 242 292 | 
             
                        end
         | 
| 293 | 
            +
                      end
         | 
| 243 294 | 
             
                    end
         | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 246 | 
            -
             | 
| 247 | 
            -
             | 
| 248 | 
            -
             | 
| 249 | 
            -
                     | 
| 250 | 
            -
                  end
         | 
| 295 | 
            +
                    unless output.string == ""
         | 
| 296 | 
            +
                      #puts '='*100
         | 
| 297 | 
            +
                      puts (' '*100).yellow.underline
         | 
| 298 | 
            +
                      puts " Diff of externals (**don't forget to commit these too!**):".ljust(100, ' ').yellow_on_red.bold.underline
         | 
| 299 | 
            +
                      puts output.string
         | 
| 300 | 
            +
                    end
         | 
| 301 | 
            +
                  end unless @ignore_externals
         | 
| 251 302 | 
             
                end
         | 
| 252 303 |  | 
| 253 304 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| @@ -268,9 +319,11 @@ module Subversion | |
| 268 319 | 
             
                      # :todo: Finish...
         | 
| 269 320 |  | 
| 270 321 | 
             
                    when nil
         | 
| 271 | 
            -
                      puts "You are using " +  | 
| 322 | 
            +
                      puts "You are using " + 
         | 
| 323 | 
            +
                           's'.green.bold + 'v'.cyan.bold + 'n'.magenta.bold + '-' + 'c'.red.bold + 'o'.cyan.bold + 'm'.blue.bold + 'm'.yellow.bold + 'a'.green.bold + 'n'.white.bold + 'd'.green.bold + ' version ' + Project::Version
         | 
| 324 | 
            +
                           ", a colorful, useful replacement/wrapper for the standard svn command."
         | 
| 272 325 | 
             
                      puts "svn-command is installed at: " + $0.bold
         | 
| 273 | 
            -
                      puts " | 
| 326 | 
            +
                      puts "You may bypass this wrapper by using the full path to svn: " + Subversion.executable.bold
         | 
| 274 327 | 
             
                      puts
         | 
| 275 328 | 
             
                      puts Subversion.help(subcommand).gsub(<<End, '')
         | 
| 276 329 |  | 
| @@ -278,7 +331,8 @@ Subversion is a tool for version control. | |
| 278 331 | 
             
            For additional information, see http://subversion.tigris.org/
         | 
| 279 332 | 
             
            End
         | 
| 280 333 |  | 
| 281 | 
            -
                      puts | 
| 334 | 
            +
                      puts
         | 
| 335 | 
            +
                      puts 'Subcommands added by svn-command (refer to '.green.underline + 'http://svn-command.rubyforge.org/'.white.underline + ' for usage details):'.green.underline
         | 
| 282 336 | 
             
                      @@subcommand_list.each do |subcommand|
         | 
| 283 337 | 
             
                        aliases_list = subcommand_aliases_list(subcommand.option_methodize.to_sym)
         | 
| 284 338 | 
             
                        aliases_list = aliases_list.empty? ? '' : ' (' + aliases_list.join(', ') + ')'
         | 
| @@ -395,6 +449,20 @@ End | |
| 395 449 | 
             
                # Custom subcommands
         | 
| 396 450 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 397 451 |  | 
| 452 | 
            +
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 453 | 
            +
                def repository_root(*args)
         | 
| 454 | 
            +
                  puts Subversion.repository_root(*args)
         | 
| 455 | 
            +
                end
         | 
| 456 | 
            +
                alias_subcommand :base_url => :repository_root
         | 
| 457 | 
            +
                alias_subcommand :root_url => :repository_root
         | 
| 458 | 
            +
             | 
| 459 | 
            +
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 460 | 
            +
                def latest_revision(*args)
         | 
| 461 | 
            +
                  puts Subversion.latest_revision
         | 
| 462 | 
            +
                end
         | 
| 463 | 
            +
                alias_subcommand :last_revision => :latest_revision
         | 
| 464 | 
            +
                alias_subcommand :head          => :latest_revision
         | 
| 465 | 
            +
             | 
| 398 466 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 399 467 |  | 
| 400 468 | 
             
                # *Experimental*
         | 
| @@ -460,8 +528,6 @@ End | |
| 460 528 | 
             
                    Subversion::Extensions.each_unadded( Subversion.status(*args) ) do |file|
         | 
| 461 529 | 
             
                      $ignore_dry_run_option = false
         | 
| 462 530 | 
             
                      begin
         | 
| 463 | 
            -
                        response = ""
         | 
| 464 | 
            -
             | 
| 465 531 | 
             
                        puts( ('-'*100).green )
         | 
| 466 532 | 
             
                        puts "What do you want to do with '#{file.white.underline}'?".white.bold
         | 
| 467 533 | 
             
                        begin
         | 
| @@ -495,7 +561,7 @@ End | |
| 495 561 | 
             
                          "or " + "any other key".white.bold + " to do nothing > "
         | 
| 496 562 | 
             
                        )
         | 
| 497 563 | 
             
                        response = ""
         | 
| 498 | 
            -
                        response = $stdin.getc.chr # while !['a', 'd', 'i', "\n"].include?(begin response.downcase!; response end)
         | 
| 564 | 
            +
                        response = $stdin.getc.chr.downcase # while !['a', 'd', 'i', "\n"].include?(begin response.downcase!; response end)
         | 
| 499 565 |  | 
| 500 566 |  | 
| 501 567 | 
             
                        case response
         | 
| @@ -512,7 +578,7 @@ End | |
| 512 578 | 
             
                                "Yes".menu_item(:red) + ", " +
         | 
| 513 579 | 
             
                                "No".menu_item(:green) + 
         | 
| 514 580 | 
             
                                " > "
         | 
| 515 | 
            -
                              response = $stdin.getc.chr while !['y', 'n', "\n"].include?(begin response.downcase!; response end)
         | 
| 581 | 
            +
                              response = $stdin.getc.chr.downcase while !['y', 'n', "\n"].include?(begin response.downcase!; response end)
         | 
| 516 582 | 
             
                            else
         | 
| 517 583 | 
             
                              response = "y"
         | 
| 518 584 | 
             
                            end
         | 
| @@ -530,10 +596,10 @@ End | |
| 530 596 | 
             
                            puts
         | 
| 531 597 | 
             
                          else
         | 
| 532 598 | 
             
                            # Skip / Do nothing with this file
         | 
| 533 | 
            -
                            puts
         | 
| 599 | 
            +
                            puts " (Skipping...)"
         | 
| 534 600 | 
             
                        end
         | 
| 535 601 | 
             
                      rescue Interrupt
         | 
| 536 | 
            -
                        puts " | 
| 602 | 
            +
                        puts "\nGoodbye!"
         | 
| 537 603 | 
             
                        throw :exit
         | 
| 538 604 | 
             
                      end
         | 
| 539 605 | 
             
                    end # each_unadded
         | 
| @@ -633,10 +699,10 @@ End | |
| 633 699 | 
             
                        begin
         | 
| 634 700 | 
             
                          #print "Press Ctrl-C to skip, any other key to continue. (This will start up your default editor.) "
         | 
| 635 701 | 
             
                          print "Do you want to edit svn:externals for this directory?".black_on_white + ' ' + 'yes'.menu_item(:white) + '/' + 'No'.menu_item(:white) + " > "
         | 
| 636 | 
            -
                          response = $stdin.getc.chr
         | 
| 637 | 
            -
                          system command if response | 
| 702 | 
            +
                          response = $stdin.getc.chr.downcase
         | 
| 703 | 
            +
                          system command if response == 'y'
         | 
| 638 704 | 
             
                        rescue Interrupt
         | 
| 639 | 
            -
                          puts " | 
| 705 | 
            +
                          puts "\nGoodbye!"
         | 
| 640 706 | 
             
                          throw :exit
         | 
| 641 707 | 
             
                        ensure
         | 
| 642 708 | 
             
                          puts
         | 
| @@ -727,17 +793,62 @@ End | |
| 727 793 | 
             
                  svn :exec, *args
         | 
| 728 794 | 
             
                end
         | 
| 729 795 |  | 
| 796 | 
            +
                # Lets you edit it with your default editor
         | 
| 797 | 
            +
                module EditRevisionProperty
         | 
| 798 | 
            +
                  def _r(revision)
         | 
| 799 | 
            +
                    @revision = revision
         | 
| 800 | 
            +
                  end
         | 
| 801 | 
            +
                end
         | 
| 802 | 
            +
                def edit_revision_property(property_name, directory = './')
         | 
| 803 | 
            +
                  args = ['propedit', '--revprop', property_name, directory]
         | 
| 804 | 
            +
                  rev = @revision ? @revision : 'head'
         | 
| 805 | 
            +
                  args.concat ['-r', rev]
         | 
| 806 | 
            +
                  Subversion.print_commands_for do
         | 
| 807 | 
            +
                    svn :system, *args
         | 
| 808 | 
            +
                  end
         | 
| 809 | 
            +
             | 
| 810 | 
            +
                  value = Subversion::get_revision_property(property_name, rev)
         | 
| 811 | 
            +
                  p value
         | 
| 812 | 
            +
             | 
| 813 | 
            +
                  # Currently there is no seperate option to *delete* a revision property (propdel)... That would be useful for those
         | 
| 814 | 
            +
                  # properties that are just boolean *flags* (set or not set).
         | 
| 815 | 
            +
                  # I'm assuming most people will very rarely if ever actually want to set a property to the empty string (''), so
         | 
| 816 | 
            +
                  # we can use the empty string as a way to trigger a propdel...
         | 
| 817 | 
            +
                  if value == ''
         | 
| 818 | 
            +
                    puts
         | 
| 819 | 
            +
                    print "Are you sure you want to delete property #{property_name}".red.bold + "'? " +
         | 
| 820 | 
            +
                      "Yes".menu_item(:red) + ", " +
         | 
| 821 | 
            +
                      "No".menu_item(:green) + 
         | 
| 822 | 
            +
                      " > "
         | 
| 823 | 
            +
                    response = ''
         | 
| 824 | 
            +
                    response = $stdin.getc.chr.downcase while !['y', 'n', "\n"].include?(begin response.downcase!; response end)
         | 
| 825 | 
            +
                    puts
         | 
| 826 | 
            +
                    if response == 'y'
         | 
| 827 | 
            +
                      Subversion.print_commands_for do
         | 
| 828 | 
            +
                        Subversion::delete_revision_property(property_name, rev)
         | 
| 829 | 
            +
                      end
         | 
| 830 | 
            +
                    end
         | 
| 831 | 
            +
                  end
         | 
| 832 | 
            +
                end
         | 
| 833 | 
            +
             | 
| 730 834 | 
             
                # Lets you edit it with your default editor
         | 
| 731 835 | 
             
                module EditMessage
         | 
| 732 836 | 
             
                  def _r(revision)
         | 
| 733 837 | 
             
                    @revision = revision
         | 
| 734 838 | 
             
                  end
         | 
| 735 839 | 
             
                end
         | 
| 736 | 
            -
                def edit_message()
         | 
| 737 | 
            -
                   | 
| 738 | 
            -
             | 
| 739 | 
            -
             | 
| 740 | 
            -
             | 
| 840 | 
            +
                def edit_message(directory = './')
         | 
| 841 | 
            +
                  edit_revision_property('svn:log', directory)
         | 
| 842 | 
            +
                end
         | 
| 843 | 
            +
             | 
| 844 | 
            +
                def edit_property(property_name, directory = './')
         | 
| 845 | 
            +
                end
         | 
| 846 | 
            +
             | 
| 847 | 
            +
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 848 | 
            +
                # Cause a working copy to cease being a working copy
         | 
| 849 | 
            +
                def delete_svn
         | 
| 850 | 
            +
                  system('find -name .svn | xargs -n1 echo')
         | 
| 851 | 
            +
                  system('find -name .svn | xargs -n1 rm -r')
         | 
| 741 852 | 
             
                end
         | 
| 742 853 |  | 
| 743 854 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| @@ -754,9 +865,191 @@ End | |
| 754 865 | 
             
                def grep_log
         | 
| 755 866 | 
             
                  raise NotImplementedError
         | 
| 756 867 | 
             
                end
         | 
| 757 | 
            -
             | 
| 758 | 
            -
             | 
| 868 | 
            +
             | 
| 869 | 
            +
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 870 | 
            +
                module Revisions
         | 
| 871 | 
            +
                  # Start at earlier revision and go forwards rather than starting at the latest revision
         | 
| 872 | 
            +
                  #def __reverse
         | 
| 873 | 
            +
                  def __forward
         | 
| 874 | 
            +
                    @reverse = true
         | 
| 875 | 
            +
                  end
         | 
| 876 | 
            +
                  def __forwards
         | 
| 877 | 
            +
                    @reverse = true
         | 
| 878 | 
            +
                  end
         | 
| 879 | 
            +
             | 
| 880 | 
            +
                  # Only show revisions that are in need of a code review
         | 
| 881 | 
            +
                  # :todo:
         | 
| 882 | 
            +
                  def __unreviewed_only
         | 
| 883 | 
            +
                    @unreviewed_only
         | 
| 884 | 
            +
                  end
         | 
| 885 | 
            +
                end
         | 
| 886 | 
            +
                # :todo: document in readme! test! release! annouce!
         | 
| 887 | 
            +
                def revisions(directory = './')
         | 
| 888 | 
            +
                  puts "Getting list of revisions for '#{directory.white.bold}' ..."
         | 
| 889 | 
            +
             | 
| 890 | 
            +
                  head = Subversion.latest_revision
         | 
| 891 | 
            +
                  revisions = Subversion.revisions(directory)
         | 
| 892 | 
            +
             | 
| 893 | 
            +
                  puts "#{revisions.length.to_s.bold} revisions found. Starting with #{@reverse ? 'oldest' : 'most recent'} revision and #{@reverse ? 'going forward in time' : 'going backward in time'}..."
         | 
| 894 | 
            +
                  revisions.instance_variable_get(:@revisions).reverse! if @reverse
         | 
| 895 | 
            +
                  revision_ids = revisions.map(&:identifier)
         | 
| 896 | 
            +
             | 
| 897 | 
            +
                  target_rev = nil # revision_ids.first
         | 
| 898 | 
            +
                  show_revision_again = true
         | 
| 899 | 
            +
                  revisions.each do |revision|
         | 
| 900 | 
            +
                    rev = revision.identifier
         | 
| 901 | 
            +
                    other_rev = rev-1
         | 
| 902 | 
            +
                    if target_rev
         | 
| 903 | 
            +
                      if rev == target_rev
         | 
| 904 | 
            +
                        target_rev = nil    # We have arrived.
         | 
| 905 | 
            +
                      else
         | 
| 906 | 
            +
                        next                # Keep going (hopefully in the right direction!)
         | 
| 907 | 
            +
                      end
         | 
| 908 | 
            +
                    end
         | 
| 909 | 
            +
             | 
| 910 | 
            +
                    # Display the revision
         | 
| 911 | 
            +
                    if show_revision_again
         | 
| 912 | 
            +
                      puts((' '*100).green.underline)
         | 
| 913 | 
            +
                      puts "#{revisions.length - revision_ids.index(rev)}. ".green.bold +
         | 
| 914 | 
            +
                        "r#{rev}".magenta.bold + (rev == head ? ' (head)'.bold : '') + 
         | 
| 915 | 
            +
                        " | #{revision.developer} | #{revision.time.strftime('%Y-%m-%d %H:%M:%S')}".magenta.bold
         | 
| 916 | 
            +
                      puts revision.message
         | 
| 917 | 
            +
                      puts
         | 
| 918 | 
            +
                      #pp revision
         | 
| 919 | 
            +
                      puts revision.map {|a|
         | 
| 920 | 
            +
                        (a.status ? a.status[0..0].colorize_svn_status_code : ' ') +   # This check is necessary because RSCM doesn't recognize several Subversion status flags, including 'R', and status will return nil in these cases.
         | 
| 921 | 
            +
                          ' ' + a.path
         | 
| 922 | 
            +
                      }.join("\n")
         | 
| 923 | 
            +
                    else
         | 
| 924 | 
            +
                      show_revision_again = true
         | 
| 925 | 
            +
                    end
         | 
| 926 | 
            +
             | 
| 927 | 
            +
                    # Display the menu
         | 
| 928 | 
            +
                    print(
         | 
| 929 | 
            +
                      'View this changeset'.menu_item(:cyan) + ', ' +
         | 
| 930 | 
            +
                      'Diff against specific revision'.menu_item(:cyan, 'D') + ', ' +
         | 
| 931 | 
            +
                      'Grep the changeset'.menu_item(:cyan, 'G') + ', ' +
         | 
| 932 | 
            +
                      'List or '.menu_item(:magenta, 'L') + '' +
         | 
| 933 | 
            +
                      'Edit revision properties'.menu_item(:magenta, 'E') + ', ' +
         | 
| 934 | 
            +
                      'svn Cat all files from revision'.menu_item(:cyan, 'C') + ', ' +
         | 
| 935 | 
            +
                      'grep the cat'.menu_item(:cyan, 'a') + ', ' + "\n  " +
         | 
| 936 | 
            +
                      'mark as Reviewed'.menu_item(:green, 'R') + ', ' +
         | 
| 937 | 
            +
                      'edit log Message'.menu_item(:yellow, 'M') + ', ' +
         | 
| 938 | 
            +
                      'or ' + 'browse using ' + 'Up/Down/Enter'.white.bold + ' keys > '
         | 
| 939 | 
            +
                    )
         | 
| 940 | 
            +
             | 
| 941 | 
            +
                    # Get response from user and then act on it
         | 
| 942 | 
            +
                    begin # rescue
         | 
| 943 | 
            +
                      response = ""
         | 
| 944 | 
            +
                      response = $stdin.getc.chr.downcase
         | 
| 945 | 
            +
             | 
| 946 | 
            +
                      # Escape sequence such as the up arrow key ("\e[A")
         | 
| 947 | 
            +
                      if response == "\e"
         | 
| 948 | 
            +
                        response << (next_char = $stdin.getc.chr)
         | 
| 949 | 
            +
                        if next_char == '['
         | 
| 950 | 
            +
                          response << (next_char = $stdin.getc.chr)
         | 
| 951 | 
            +
                        end
         | 
| 952 | 
            +
                      end
         | 
| 953 | 
            +
             | 
| 954 | 
            +
                      if response == 'd' # diff against Other revision
         | 
| 955 | 
            +
                        response = 'v'
         | 
| 956 | 
            +
                        puts
         | 
| 957 | 
            +
                        print 'All right, which revision shall it be then? '.bold + ' (backspace not currently supported)? '
         | 
| 958 | 
            +
                        other_rev = $stdin.gets.chomp.to_i
         | 
| 959 | 
            +
                      end
         | 
| 960 | 
            +
             | 
| 961 | 
            +
                      case response
         | 
| 962 | 
            +
                        when 'v'  # Diff
         | 
| 963 | 
            +
                          revs_to_compare = [other_rev, rev]
         | 
| 964 | 
            +
                          puts "\n"*10
         | 
| 965 | 
            +
                          puts((' '*100).green.underline)
         | 
| 966 | 
            +
                          print "Diffing #{revs_to_compare.min}:#{revs_to_compare.max}... ".bold
         | 
| 967 | 
            +
                          puts
         | 
| 968 | 
            +
                          #Subversion.repository_root
         | 
| 969 | 
            +
                          SvnCommand.execute("diff #{directory} --ignore-externals -r #{revs_to_compare.min}:#{revs_to_compare.max}")
         | 
| 970 | 
            +
             | 
| 971 | 
            +
                        when 'g'  # Grep the changeset
         | 
| 972 | 
            +
                          revs_to_compare = [other_rev, rev]
         | 
| 973 | 
            +
                          puts
         | 
| 974 | 
            +
                          puts((' '*100).green.underline)
         | 
| 975 | 
            +
                          print "Diffing #{revs_to_compare.min}:#{revs_to_compare.max}... ".bold
         | 
| 976 | 
            +
                          puts
         | 
| 977 | 
            +
                          puts Extensions.diff(directory, '-r', "#{revs_to_compare.min}:#{revs_to_compare.max}")
         | 
| 978 | 
            +
             | 
| 979 | 
            +
                        when 'l'  # List revision properties
         | 
| 980 | 
            +
                          puts
         | 
| 981 | 
            +
                          puts Subversion::Extensions::printable_revision_properties(rev)
         | 
| 982 | 
            +
                          show_revision_again = false
         | 
| 983 | 
            +
             | 
| 984 | 
            +
                        when 'e'  # Edit revision property
         | 
| 985 | 
            +
                          puts
         | 
| 986 | 
            +
                          puts Subversion::Extensions::printable_revision_properties(rev)
         | 
| 987 | 
            +
                          puts "Warning: These properties are *not* under version control! Try not to permanently destroy anything *too* important...".red.bold
         | 
| 988 | 
            +
                          puts "Note: If you want to *delete* a property, simply set its value to '' and it will be deleted (propdel) for you."
         | 
| 989 | 
            +
                          print 'Which property would you like to edit'.bold + ' (backspace not currently supported)? '
         | 
| 990 | 
            +
                          property_name = $stdin.gets.chomp
         | 
| 991 | 
            +
                          unless property_name == ''
         | 
| 992 | 
            +
                            Subversion.print_commands_for do
         | 
| 993 | 
            +
                              @revision = rev
         | 
| 994 | 
            +
                              edit_revision_property(property_name, directory)
         | 
| 995 | 
            +
                            end
         | 
| 996 | 
            +
                          end
         | 
| 997 | 
            +
             | 
| 998 | 
            +
                          show_revision_again = false
         | 
| 999 | 
            +
             | 
| 1000 | 
            +
                        when 'r'  # Mark as reviewed
         | 
| 1001 | 
            +
                          puts
         | 
| 1002 | 
            +
                          your_name = ENV['USER'] # I would use the same username that Subversion itself would use if you committed
         | 
| 1003 | 
            +
                                                  # something (since it is sometimes different from your system username), but I don't know
         | 
| 1004 | 
            +
                                                  # how to retrieve that (except by poking around in your ~/.subversion/ directory, but
         | 
| 1005 | 
            +
                                                  # that seems kind of rude...).
         | 
| 1006 | 
            +
                          puts "Marking as reviewed by '#{your_name}'..."
         | 
| 1007 | 
            +
                          Subversion.print_commands_for do
         | 
| 1008 | 
            +
                            puts svn(:capture, "propset code:reviewed '#{your_name}' --revprop -r #{rev}", :prepare_args => false)
         | 
| 1009 | 
            +
                            # :todo: Maybe *append* to code:reviewed (,-delimited) rather than overwriting it?, in case there is a policy of requiring 2 reviewers or something
         | 
| 1010 | 
            +
                          end
         | 
| 1011 | 
            +
             | 
| 1012 | 
            +
                        when 'm'  # Edit log message
         | 
| 1013 | 
            +
                          puts
         | 
| 1014 | 
            +
                          Subversion.print_commands_for do
         | 
| 1015 | 
            +
                            SvnCommand.execute("edit_message -r #{rev}")
         | 
| 1016 | 
            +
                          end
         | 
| 1017 | 
            +
             | 
| 1018 | 
            +
                        when "\e[A" # Up
         | 
| 1019 | 
            +
                          i = revision_ids.index(rev)
         | 
| 1020 | 
            +
                          target_rev = revision_ids[i - 1]
         | 
| 1021 | 
            +
                          puts " Previous..."
         | 
| 1022 | 
            +
                          retry
         | 
| 1023 | 
            +
             | 
| 1024 | 
            +
                          
         | 
| 1025 | 
            +
                        when /\n|\e\[B/ # Enter or Down
         | 
| 1026 | 
            +
                          # Skip / Do nothing with this file
         | 
| 1027 | 
            +
                          puts " Next..."
         | 
| 1028 | 
            +
                          next
         | 
| 1029 | 
            +
             | 
| 1030 | 
            +
                        else
         | 
| 1031 | 
            +
                          # Invalid option. Do nothing.
         | 
| 1032 | 
            +
                          #puts response.inspect
         | 
| 1033 | 
            +
                          puts
         | 
| 1034 | 
            +
                          show_revision_again = false
         | 
| 1035 | 
            +
             | 
| 1036 | 
            +
                      end # case response
         | 
| 1037 | 
            +
             | 
| 1038 | 
            +
                      redo # Until they tell us they're ready to move on...
         | 
| 1039 | 
            +
             | 
| 1040 | 
            +
                    rescue Interrupt
         | 
| 1041 | 
            +
                      puts "\nGoodbye!"
         | 
| 1042 | 
            +
                      return
         | 
| 1043 | 
            +
                    end # rescue
         | 
| 1044 | 
            +
                  end
         | 
| 759 1045 | 
             
                end
         | 
| 1046 | 
            +
                alias_subcommand :changesets        => :revisions
         | 
| 1047 | 
            +
                alias_subcommand :browse            => :revisions
         | 
| 1048 | 
            +
                alias_subcommand :browse_log        => :revisions
         | 
| 1049 | 
            +
                alias_subcommand :browse_revisions  => :revisions
         | 
| 1050 | 
            +
                alias_subcommand :browse_changesets => :revisions
         | 
| 1051 | 
            +
                # See also the implementation of revisions() in /usr/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/scm/subversion.rb
         | 
| 1052 | 
            +
                # Other name ideas: browse, list_commits, changeset_browser, log_browser, interactive_log
         | 
| 760 1053 |  | 
| 761 1054 | 
             
                #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 762 1055 | 
             
                # Aliases
         | 
| @@ -794,7 +1087,8 @@ End | |
| 794 1087 | 
             
                end
         | 
| 795 1088 |  | 
| 796 1089 | 
             
                def prepare_args(args)
         | 
| 797 | 
            -
                  args.compact! | 
| 1090 | 
            +
                  args.compact!       # nil elements spell trouble
         | 
| 1091 | 
            +
                  args.map!(&:to_s)   # shell_escape doesn't like Fixnums either
         | 
| 798 1092 | 
             
                  @passthrough_options + args.shell_escape
         | 
| 799 1093 | 
             
                end
         | 
| 800 1094 | 
             
                # To allow testing/stubbing
         | 
    
        data/test/subversion_test.rb
    CHANGED
    
    | @@ -96,4 +96,48 @@ class SubversionTest < Test::Unit::TestCase | |
| 96 96 | 
             
                assert_equal "svn status foo", Subversion.executed.first
         | 
| 97 97 | 
             
              end
         | 
| 98 98 |  | 
| 99 | 
            +
              def test_revisions
         | 
| 100 | 
            +
                Subversion.stubs(:log).returns(<<End)
         | 
| 101 | 
            +
            ------------------------------------------------------------------------
         | 
| 102 | 
            +
            r407119 | moo | 2006-05-16 20:27:28 -0500 (Tue, 16 May 2006) | 5 lines
         | 
| 103 | 
            +
            Changed paths:
         | 
| 104 | 
            +
               M /trunk/cow/black.rb
         | 
| 105 | 
            +
               M /trunk/cow/brown.rb
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            Commit message...
         | 
| 108 | 
            +
            ------------------------------------------------------------------------
         | 
| 109 | 
            +
            End
         | 
| 110 | 
            +
                assert_equal 1, Subversion.revisions.length
         | 
| 111 | 
            +
                assert_equal RSCM::Revision, Subversion.revisions[0].class
         | 
| 112 | 
            +
                assert_equal RSCM::RevisionFile, Subversion.revisions[0][0].class
         | 
| 113 | 
            +
                assert_equal 'modified', Subversion.revisions[0][0].status.downcase
         | 
| 114 | 
            +
                assert_equal 'trunk/cow/black.rb', Subversion.revisions[0][0].path
         | 
| 115 | 
            +
                assert_equal 'trunk/cow/brown.rb', Subversion.revisions[0][1].path
         | 
| 116 | 
            +
                assert_equal 'Commit message...', Subversion.revisions[0].message
         | 
| 117 | 
            +
              end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
              def test_revision_properties_names
         | 
| 120 | 
            +
                Subversion.stubs(:proplist).returns(<<End)
         | 
| 121 | 
            +
            Unversioned properties on revision 2819:
         | 
| 122 | 
            +
              svn:log
         | 
| 123 | 
            +
              svn:author
         | 
| 124 | 
            +
              svn:date
         | 
| 125 | 
            +
            End
         | 
| 126 | 
            +
                assert_equal ['svn:log', 'svn:author', 'svn:date'], Subversion.revision_properties_names(rev = 14)
         | 
| 127 | 
            +
              end
         | 
| 128 | 
            +
              def test_revision_properties
         | 
| 129 | 
            +
                Subversion.stubs(:proplist).returns(<<End)
         | 
| 130 | 
            +
            Unversioned properties on revision 2819:
         | 
| 131 | 
            +
              svn:log
         | 
| 132 | 
            +
              svn:author
         | 
| 133 | 
            +
              svn:date
         | 
| 134 | 
            +
            End
         | 
| 135 | 
            +
                Subversion.stubs(:get_revision_property).returns('a value')
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                assert_equal [
         | 
| 138 | 
            +
                  Subversion::RevisionProperty.new('svn:log', 'a value'),
         | 
| 139 | 
            +
                  Subversion::RevisionProperty.new('svn:author', 'a value'),
         | 
| 140 | 
            +
                  Subversion::RevisionProperty.new('svn:date', 'a value'),
         | 
| 141 | 
            +
                ], Subversion.revision_properties(rev = 14)
         | 
| 142 | 
            +
              end
         | 
| 99 143 | 
             
            end
         | 
    
        data/test/svn_command_test.rb
    CHANGED
    
    | @@ -4,12 +4,62 @@ require_local '../lib/svn-command/svn_command.rb' | |
| 4 4 | 
             
            require 'facets/core/string/to_re'
         | 
| 5 5 | 
             
            require 'yaml'
         | 
| 6 6 |  | 
| 7 | 
            +
             | 
| 8 | 
            +
            require 'facets/core/module/alias_method_chain'
         | 
| 9 | 
            +
            require 'qualitysmith_extensions/string/each_char_with_index'
         | 
| 10 | 
            +
            module Test
         | 
| 11 | 
            +
              module Unit
         | 
| 12 | 
            +
                module Assertions
         | 
| 13 | 
            +
                  # Rather than showing the expected and the actual and asking the user to figure out the commonalities and differences
         | 
| 14 | 
            +
                  # himself, this method will highlight the differences for the user (in color), so that they can be spotted in less than
         | 
| 15 | 
            +
                  # an instant!
         | 
| 16 | 
            +
                  # Strings: show common characters in a plain color and highlight the characters that differ (at that index) in yellow.
         | 
| 17 | 
            +
                  # Strings: show common elements in a plain color and highlight the elements that differ (at that index) in yellow.
         | 
| 18 | 
            +
                  def assert_equal_with_difference_highlighting(expected, actual, message=nil)
         | 
| 19 | 
            +
                    String.class_eval do
         | 
| 20 | 
            +
                      alias_method :colorize, :colorize_without_no_color
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                    if String===expected && String===actual
         | 
| 23 | 
            +
                      expected_with_highlighting = ''
         | 
| 24 | 
            +
                      actual_with_highlighting = ''
         | 
| 25 | 
            +
                      expected.each_char_with_index do |i, c|
         | 
| 26 | 
            +
                        if c != actual[i].chr
         | 
| 27 | 
            +
                          expected_with_highlighting << c.yellow
         | 
| 28 | 
            +
                          actual_with_highlighting   << actual[i].chr.yellow
         | 
| 29 | 
            +
                        else
         | 
| 30 | 
            +
                          expected_with_highlighting << c
         | 
| 31 | 
            +
                          actual_with_highlighting   << c
         | 
| 32 | 
            +
                        end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                      full_message = build_message(message, <<End, expected_with_highlighting, actual_with_highlighting)
         | 
| 36 | 
            +
            <?>
         | 
| 37 | 
            +
            expected but was
         | 
| 38 | 
            +
            <?>.
         | 
| 39 | 
            +
            End
         | 
| 40 | 
            +
                      puts actual_with_highlighting if expected != actual
         | 
| 41 | 
            +
                      assert_block(full_message) { expected == actual }
         | 
| 42 | 
            +
                    else
         | 
| 43 | 
            +
                      assert_equal_without_difference_highlighting(expected, actual, message)
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                    String.class_eval do
         | 
| 46 | 
            +
                      alias_method :colorize, :colorize_with_no_color
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end # def assert_equal_with_difference_highlighting
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  alias_method_chain :assert_equal, :difference_highlighting
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
             | 
| 7 56 | 
             
            Subversion.color = false
         | 
| 8 57 | 
             
            # Makes testing simpler. We can test all the *colorization* features via *manual* testing (since they're not as critical).
         | 
| 9 58 | 
             
            class String
         | 
| 10 | 
            -
              def  | 
| 59 | 
            +
              def colorize_with_no_color(string, options = {})
         | 
| 11 60 | 
             
                string
         | 
| 12 61 | 
             
              end
         | 
| 62 | 
            +
              alias_method_chain :colorize, :no_color
         | 
| 13 63 | 
             
            end
         | 
| 14 64 |  | 
| 15 65 | 
             
            module Subversion
         | 
| @@ -443,10 +493,28 @@ end | |
| 443 493 | 
             
            class SvnEditMessageTest < BaseSvnCommandTest
         | 
| 444 494 | 
             
              def test_1
         | 
| 445 495 | 
             
                Subversion.stubs(:status_against_server).returns("Status against revision:     56")
         | 
| 496 | 
            +
                Subversion.stubs(:get_revision_property).returns("The value I just set it to using vim, my favorite editor")
         | 
| 446 497 | 
             
                output = simulate_input('i') do
         | 
| 447 498 | 
             
                  capture_output { SvnCommand.execute('edit_message') }
         | 
| 448 499 | 
             
                end
         | 
| 449 | 
            -
                assert_equal ["svn propedit --revprop svn:log -r head"], Subversion.executed
         | 
| 500 | 
            +
                assert_equal ["svn propedit --revprop svn:log ./ -r head"], Subversion.executed
         | 
| 501 | 
            +
              end
         | 
| 502 | 
            +
            end
         | 
| 503 | 
            +
             | 
| 504 | 
            +
            class SvnEditMessageTest < BaseSvnCommandTest
         | 
| 505 | 
            +
              def test_can_actually_delete_property_too
         | 
| 506 | 
            +
                Subversion.stubs(:status_against_server).returns("Status against revision:     56")
         | 
| 507 | 
            +
                Subversion.stubs(:get_revision_property).returns("")
         | 
| 508 | 
            +
                output = simulate_input(
         | 
| 509 | 
            +
                  'y'      # Yes I'm sure I want to delete the svn:fooo property for this revision.
         | 
| 510 | 
            +
                ) do
         | 
| 511 | 
            +
                  capture_output { SvnCommand.execute('edit_revision_property svn:foo') }
         | 
| 512 | 
            +
                end
         | 
| 513 | 
            +
                assert_match /Are you sure you want to delete/, output
         | 
| 514 | 
            +
                assert_equal [
         | 
| 515 | 
            +
                  "svn propedit --revprop svn:foo ./ -r head",
         | 
| 516 | 
            +
                  "svn propdel --revprop svn:foo -r head"
         | 
| 517 | 
            +
                ], Subversion.executed
         | 
| 450 518 | 
             
              end
         | 
| 451 519 | 
             
            end
         | 
| 452 520 |  | 
| @@ -493,6 +561,104 @@ class SvnViewCommitsTest < BaseSvnCommandTest | |
| 493 561 | 
             
              end
         | 
| 494 562 | 
             
            end
         | 
| 495 563 |  | 
| 564 | 
            +
            #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 565 | 
            +
            # Changeset/commit/log Browser
         | 
| 566 | 
            +
             | 
| 567 | 
            +
            class SvnRevisionsTest < BaseSvnCommandTest
         | 
| 568 | 
            +
              def test_1
         | 
| 569 | 
            +
                Subversion.stubs(:revisions).returns(
         | 
| 570 | 
            +
                  begin
         | 
| 571 | 
            +
                    RSCM::Revisions.class_eval do
         | 
| 572 | 
            +
                      attr_accessor :revisions
         | 
| 573 | 
            +
                    end
         | 
| 574 | 
            +
                    RSCM::Revision.class_eval do
         | 
| 575 | 
            +
                      attr_accessor :files
         | 
| 576 | 
            +
                    end
         | 
| 577 | 
            +
             | 
| 578 | 
            +
                    file1 = RSCM::RevisionFile.new
         | 
| 579 | 
            +
                    file1.status = 'added'.upcase
         | 
| 580 | 
            +
                    file1.path = 'dir/file1'
         | 
| 581 | 
            +
                    file2 = RSCM::RevisionFile.new
         | 
| 582 | 
            +
                    file2.status = 'modified'.upcase
         | 
| 583 | 
            +
                    file2.path = 'dir/file2'
         | 
| 584 | 
            +
             | 
| 585 | 
            +
                    revision1 = RSCM::Revision.new
         | 
| 586 | 
            +
                    revision1.identifier = 1800
         | 
| 587 | 
            +
                    revision1.developer = 'tyler'
         | 
| 588 | 
            +
                    revision1.time = Time.utc(2007, 12, 01)
         | 
| 589 | 
            +
                    revision1.message = 'I say! Quite the storm, what!'
         | 
| 590 | 
            +
                    revision1.files = [file1, file2]
         | 
| 591 | 
            +
             | 
| 592 | 
            +
                    revision2 = RSCM::Revision.new
         | 
| 593 | 
            +
                    revision2.identifier = 1801
         | 
| 594 | 
            +
                    revision2.developer = 'tyler'
         | 
| 595 | 
            +
                    revision2.time = Time.utc(2007, 12, 02)
         | 
| 596 | 
            +
                    revision2.message = 'These Romans are crazy!'
         | 
| 597 | 
            +
                    revision2.files = [file2]
         | 
| 598 | 
            +
             | 
| 599 | 
            +
                    revisions = RSCM::Revisions.new
         | 
| 600 | 
            +
                    revisions.revisions = [revision1, revision2]
         | 
| 601 | 
            +
                  end
         | 
| 602 | 
            +
                )
         | 
| 603 | 
            +
                Subversion.stubs(:diff).returns("the diff")
         | 
| 604 | 
            +
                
         | 
| 605 | 
            +
                output = simulate_input(
         | 
| 606 | 
            +
                  'v' +     # View this changeset
         | 
| 607 | 
            +
                  "\n" +    # Continue to revision 1800
         | 
| 608 | 
            +
                  "\n"      # Try to continue, but of course there won't be any more revisions, so it will exit.
         | 
| 609 | 
            +
                ) do
         | 
| 610 | 
            +
                  capture_output { SvnCommand.execute('revisions') }
         | 
| 611 | 
            +
                end
         | 
| 612 | 
            +
                puts output
         | 
| 613 | 
            +
                require 'unroller'
         | 
| 614 | 
            +
                #Unroller::trace :exclude_classes => /PP|PrettyPrint/ do
         | 
| 615 | 
            +
             | 
| 616 | 
            +
                assert_equal <<-End, output
         | 
| 617 | 
            +
            Getting list of revisions for './' ...
         | 
| 618 | 
            +
            2 revisions found. Starting with most recent revision and going backward in time...
         | 
| 619 | 
            +
             | 
| 620 | 
            +
            2. r1800 | tyler | 2007-12-01 00:00:00
         | 
| 621 | 
            +
            I say! Quite the storm, what!
         | 
| 622 | 
            +
             | 
| 623 | 
            +
            A dir/file1
         | 
| 624 | 
            +
            M dir/file2
         | 
| 625 | 
            +
            View this changeset, Diff against specific revision, Grep the changeset, List or Edit revision properties, svn Cat all files from revision, grep the cat,
         | 
| 626 | 
            +
              mark as Reviewed, edit log Message, or browse using Up/Down/Enter keys >
         | 
| 627 | 
            +
             | 
| 628 | 
            +
             | 
| 629 | 
            +
             | 
| 630 | 
            +
             | 
| 631 | 
            +
             | 
| 632 | 
            +
             | 
| 633 | 
            +
             | 
| 634 | 
            +
             | 
| 635 | 
            +
             | 
| 636 | 
            +
             | 
| 637 | 
            +
            Diffing 1799:1800...
         | 
| 638 | 
            +
            the diff
         | 
| 639 | 
            +
             | 
| 640 | 
            +
            2. r1800 | tyler | 2007-12-01 00:00:00
         | 
| 641 | 
            +
            I say! Quite the storm, what!
         | 
| 642 | 
            +
             | 
| 643 | 
            +
            A dir/file1
         | 
| 644 | 
            +
            M dir/file2
         | 
| 645 | 
            +
            View this changeset, Diff against specific revision, Grep the changeset, List or Edit revision properties, svn Cat all files from revision, grep the cat,
         | 
| 646 | 
            +
              mark as Reviewed, edit log Message, or browse using Up/Down/Enter keys >  Next...
         | 
| 647 | 
            +
             | 
| 648 | 
            +
            1. r1801 | tyler | 2007-12-02 00:00:00
         | 
| 649 | 
            +
            These Romans are crazy!
         | 
| 650 | 
            +
             | 
| 651 | 
            +
            M dir/file2
         | 
| 652 | 
            +
            View this changeset, Diff against specific revision, Grep the changeset, List or Edit revision properties, svn Cat all files from revision, grep the cat,
         | 
| 653 | 
            +
              mark as Reviewed, edit log Message, or browse using Up/Down/Enter keys >  Next...
         | 
| 654 | 
            +
            End
         | 
| 655 | 
            +
                #end
         | 
| 656 | 
            +
                assert_equal [
         | 
| 657 | 
            +
                  "svn status -u ./"    # To find head
         | 
| 658 | 
            +
                ], Subversion.executed
         | 
| 659 | 
            +
              end
         | 
| 660 | 
            +
            end
         | 
| 661 | 
            +
             | 
| 496 662 |  | 
| 497 663 | 
             
            #-----------------------------------------------------------------------------------------------------------------------------
         | 
| 498 664 | 
             
            end #module Subversion
         | 
    
        metadata
    CHANGED
    
    | @@ -3,8 +3,8 @@ rubygems_version: 0.9.2 | |
| 3 3 | 
             
            specification_version: 1
         | 
| 4 4 | 
             
            name: svn-command
         | 
| 5 5 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 6 | 
            -
              version: 0. | 
| 7 | 
            -
            date: 2007-04- | 
| 6 | 
            +
              version: 0.2.1
         | 
| 7 | 
            +
            date: 2007-04-26 00:00:00 -07:00
         | 
| 8 8 | 
             
            summary: A nifty wrapper command for Subversion's command-line svn client
         | 
| 9 9 | 
             
            require_paths: 
         | 
| 10 10 | 
             
            - lib
         | 
| @@ -25,7 +25,11 @@ required_ruby_version: !ruby/object:Gem::Version::Requirement | |
| 25 25 | 
             
            platform: ruby
         | 
| 26 26 | 
             
            signing_key: 
         | 
| 27 27 | 
             
            cert_chain: 
         | 
| 28 | 
            -
            post_install_message: 
         | 
| 28 | 
            +
            post_install_message: |
         | 
| 29 | 
            +
              ---------------------------------------------------------------------------------------------------
         | 
| 30 | 
            +
              Please run _svn_command_post_install to finalize the installation.
         | 
| 31 | 
            +
              ---------------------------------------------------------------------------------------------------
         | 
| 32 | 
            +
             | 
| 29 33 | 
             
            authors: 
         | 
| 30 34 | 
             
            - Tyler Rick
         | 
| 31 35 | 
             
            files: 
         |