svn-command 0.2.7 → 0.2.12
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/ProjectInfo.rb +1 -2
- data/Readme +52 -11
- data/lib/svn-command/subversion.rb +35 -7
- data/lib/svn-command/subversion_extensions.rb +73 -67
- data/lib/svn-command/svn_command.rb +343 -77
- data/test/svn_command_test.rb +74 -47
- metadata +2 -11
data/ProjectInfo.rb
CHANGED
@@ -5,7 +5,7 @@ module Project
|
|
5
5
|
PrettyName = "Enhanced Subversion Command"
|
6
6
|
Name = "svn-command"
|
7
7
|
RubyForgeName = "svn-command"
|
8
|
-
Version = "0.2.
|
8
|
+
Version = "0.2.12"
|
9
9
|
Specification = Gem::Specification.new do |s|
|
10
10
|
s.name = Project::Name
|
11
11
|
s.summary = "A nifty wrapper command for Subversion's command-line svn client"
|
@@ -22,7 +22,6 @@ module Project
|
|
22
22
|
s.add_dependency("colored")
|
23
23
|
s.add_dependency("escape")
|
24
24
|
s.add_dependency("facets")
|
25
|
-
s.add_dependency("extensions")
|
26
25
|
s.add_dependency("qualitysmith_extensions")
|
27
26
|
s.add_dependency("rscm")
|
28
27
|
s.post_install_message = <<-End
|
data/Readme
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
= <i>Enhanced Subversion command</i> -- an +svn+ command wrapper
|
2
2
|
|
3
|
-
[
|
3
|
+
[<b>Environment</b>:] Command line (text based)
|
4
4
|
[<b>Home page</b>:] http://svn-command.rubyforge.org/
|
5
5
|
[<b>Project site</b>:] http://rubyforge.org/projects/svn-command
|
6
|
-
[<b>
|
6
|
+
[<b>Gem install</b>:] <tt>gem install svn-command</tt>
|
7
7
|
[<b>Author</b>:] Tyler Rick
|
8
8
|
[<b>Copyright</b>:] 2007 QualitySmith, Inc.
|
9
9
|
[<b>License</b>:] {GNU General Public License}[http://www.gnu.org/copyleft/gpl.html]
|
@@ -127,7 +127,7 @@ Here are a couple things you might use it for:
|
|
127
127
|
* Rather than looking at <tt>svn log -v</tt> (which can be _huge_) directly and then manually calculating revision numbers and doing things like <tt>svn diff -r1492:1493</tt> over and over, you can simply start up <tt>svn revisions</tt>, browse to the revision you're interested in using the Up/Down arrow keys, and press D to get a diff for the selected changeset.
|
128
128
|
* <b>See what's been committed since the last public release</b>. So that you can list it in your release notes, for example...
|
129
129
|
* <b>Review other people's code</b>. (There's even a mark-as-reviewed feature*, if you want to keep track of which revisions have been reviewed...)
|
130
|
-
* <b>
|
130
|
+
* <b>Search for a change you know you've _made_</b> but just don't remember what revision it was in. (Hint: Use the "grep this changeset" feature.)
|
131
131
|
* Figure out what the <b>difference is between two branches</b>.
|
132
132
|
|
133
133
|
Defaults to latest-first, but you can pass it the <tt>--forwards</tt> flag to browse from the other direction (start at the <i>oldest revision</i> and step forwards through time).
|
@@ -229,6 +229,14 @@ You can do this:
|
|
229
229
|
or just this:
|
230
230
|
svn edit_message
|
231
231
|
|
232
|
+
== <tt>svn ignore</tt>
|
233
|
+
|
234
|
+
If you want to add '*' to the list of ignored files for a directory, be sure to enclose the argument in single quotes (<tt>'</tt>) so that the shell doesn't expand the <tt>*</tt> symbol for you.
|
235
|
+
|
236
|
+
Example:
|
237
|
+
|
238
|
+
svn ignore 'tmp/sessions/*'
|
239
|
+
|
232
240
|
== <tt>svn move</tt>
|
233
241
|
|
234
242
|
You can now do commands like this:
|
@@ -391,6 +399,19 @@ But... as with most things written in Ruby, it's all more about *productivity* t
|
|
391
399
|
|
392
400
|
==To do
|
393
401
|
|
402
|
+
Calling "Extensions.anything" is stupid. Can't we just merge/extend/include the methods from Extensions into the Subversion module itself?
|
403
|
+
|
404
|
+
Say you just did `svn mv base-suffix base-new_suffix`. Now say you want to commit that move without committing anything else in that dir.
|
405
|
+
You'd think you could just do `svn ci base-*`, but no. That doesn't get base-suffix because it has been removed from the file system (and scheduled for deletion).
|
406
|
+
Can we make it so a '*' (or any glob) (escaped so shell doesn't get it) actually looks at all files returned by `svn st` (which includes those scheduled for deletion, D) that match that glob rather than all files, rather than the glob that the *shell* would do?
|
407
|
+
|
408
|
+
Say you cp'd a file A to B. You make some modifications to it and later decide to add it. Wouldn't it be nice if you could retroactively cause B to inherit the ancestry of A (as if you had svn cp'd it instead of cp'd it to begin with)?
|
409
|
+
I propose a copy_ancestry_from / imbue_with_ancestry_from command, so that you can do svn copy_ancestry_from B A that does just that.
|
410
|
+
Then you could also svn rm A and it would be (I think) completely equivalent to having done an svn mv A B in the first place.
|
411
|
+
|
412
|
+
svn list_conflicts instead of:
|
413
|
+
svn st --no-color | grep "^C"
|
414
|
+
|
394
415
|
Take the best ideas from these and incorporate:
|
395
416
|
* /usr/lib/ruby/gems/1.8/gems/rscm-0.5.1/lib/rscm/scm/subversion.rb
|
396
417
|
* /usr/lib/ruby/gems/1.8/gems/lazysvn-0.1.3/lib/subversion.rb
|
@@ -399,13 +420,33 @@ Possibly switch to LazySvn.
|
|
399
420
|
|
400
421
|
After you save/edit/set an svn:externals, it should try to automatically pretty up the margins/alignment for you.
|
401
422
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
If there is an error during an update, such as this one:
|
406
|
-
Fetching external item into 'glass/rails_backend/vendor/plugins/our_extensions'
|
407
|
-
svn: REPORT request failed on '/!svn/vcc/default'
|
408
|
-
svn: Cannot replace a directory from within
|
409
|
-
it will not be displayed on screen. At least if it's an external that had the error.
|
423
|
+
/usr/lib/ruby/gems/1.8/gems/piston-1.3.3/lib/piston/commands/import.rb has interesting way of parsing output from `svn info`
|
424
|
+
my_info = YAML::load(svn(:info, File.join(dir, '..')))
|
425
|
+
my_revision = YAML::load(svn(:info, my_info['URL']))['Revision']
|
410
426
|
|
411
427
|
More at: http://wiki.qualitysmith.com/svn-command
|
428
|
+
|
429
|
+
===Ideas from TortoiseSvn
|
430
|
+
|
431
|
+
When you drag and drop one or more files to a WC directory, it prompts you with a context menu with these options:
|
432
|
+
* svn move versioned files here
|
433
|
+
* svn copy versioned files here
|
434
|
+
* svn copy and rename versioned files here
|
435
|
+
* svn add files to this WC
|
436
|
+
* svn export to here
|
437
|
+
* svn export all to here
|
438
|
+
|
439
|
+
===Pick a better name!
|
440
|
+
|
441
|
+
svn-command doesn't say anything to differentiate it from the built-in svn command. In fact, like I did just there, you can call the original 'svn' "the svn command" too, which makes the current name highly confusing.
|
442
|
+
|
443
|
+
Word ideas to possibly incorporate:
|
444
|
+
* improved
|
445
|
+
* enhanced
|
446
|
+
* wrapper
|
447
|
+
* color
|
448
|
+
* plus
|
449
|
+
* more
|
450
|
+
|
451
|
+
A play on the word sub?
|
452
|
+
* 'subwrap'? Both a sub and wrap can be considered food items.
|
@@ -43,6 +43,7 @@ module Subversion
|
|
43
43
|
# If true, will print all commands to the screen before executing them.
|
44
44
|
@@print_commands = false
|
45
45
|
mattr_accessor :print_commands
|
46
|
+
mguard_method :print_commands!, :@@print_commands
|
46
47
|
|
47
48
|
# Adds the given items to the repository. Items may contain wildcards.
|
48
49
|
def self.add(*args)
|
@@ -324,7 +325,7 @@ module Subversion
|
|
324
325
|
matches = info(path_or_url).match(/^Repository Root: (.+)/)
|
325
326
|
matches && matches[1]
|
326
327
|
|
327
|
-
# It appears that we might need to use this old way (which looks at 'URL'), since there is actually a
|
328
|
+
# It appears that we might need to use this old way (which looks at 'URL'), since there is actually a handy property called "Repository Root" that we can look at.
|
328
329
|
# base_url = nil # needed so that base_url variable isn't local to loop block (and reset during next iteration)!
|
329
330
|
# started_using_dot_dots = false
|
330
331
|
# loop do
|
@@ -348,6 +349,39 @@ module Subversion
|
|
348
349
|
def self.root_url(*args); base_url(*args); end
|
349
350
|
def self.repository_root(*args); base_url(*args); end
|
350
351
|
|
352
|
+
|
353
|
+
def self.repository_uuid(path_or_url = './')
|
354
|
+
matches = info(path_or_url).match(/^Repository UUID: (.+)/)
|
355
|
+
matches && matches[1]
|
356
|
+
end
|
357
|
+
|
358
|
+
# By default, if you query a directory that is scheduled for addition but hasn't been committed yet (node doesn't have a UUID),
|
359
|
+
# then we will still return true, because it is *scheduled* to be under version control. If you want a stricter definition,
|
360
|
+
# and only want it to return true if the file exists in the *repository* (has a UUID)@ then pass strict = true
|
361
|
+
def self.under_version_control?(file = './', strict = false)
|
362
|
+
if strict
|
363
|
+
!!repository_uuid(file)
|
364
|
+
else # (scheduled_for_addition_counts_as_true)
|
365
|
+
!!url(file)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
def self.working_copy_root(directory = './')
|
369
|
+
uuid = repository_uuid(directory)
|
370
|
+
return nil if uuid.nil?
|
371
|
+
|
372
|
+
loop do
|
373
|
+
# Keep going up, one level at a time, ...
|
374
|
+
new_directory = File.expand_path(File.join(directory, '..'))
|
375
|
+
new_uuid = repository_uuid(new_directory)
|
376
|
+
|
377
|
+
# Until we get back a uuid that is nil (it's not a working copy at all) or different (you can have a working copy A inside of a different WC B)...
|
378
|
+
break if new_uuid.nil? or new_uuid != uuid
|
379
|
+
|
380
|
+
directory = new_directory
|
381
|
+
end
|
382
|
+
directory
|
383
|
+
end
|
384
|
+
|
351
385
|
# The location of the executable to be used
|
352
386
|
def self.executable
|
353
387
|
@@executable ||=
|
@@ -363,12 +397,6 @@ module Subversion
|
|
363
397
|
#
|
364
398
|
end
|
365
399
|
|
366
|
-
def self.print_commands_for(&block)
|
367
|
-
old_print_commands, @@print_commands = @@print_commands, true
|
368
|
-
yield
|
369
|
-
@@print_commands = old_print_commands
|
370
|
-
end
|
371
|
-
|
372
400
|
protected
|
373
401
|
def self.execute(*args)
|
374
402
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
@@ -3,6 +3,8 @@
|
|
3
3
|
gem 'colored'
|
4
4
|
require 'colored'
|
5
5
|
|
6
|
+
require 'facets/core/module/class_extension'
|
7
|
+
|
6
8
|
class Array
|
7
9
|
def to_regexp_char_class
|
8
10
|
"[#{join('')}]"
|
@@ -70,73 +72,77 @@ module Subversion
|
|
70
72
|
Uninteresting_status_flags = ["X", "W"]
|
71
73
|
Status_flags = Interesting_status_flags | Uninteresting_status_flags
|
72
74
|
|
73
|
-
|
74
|
-
input = (input || "").reject { |line|
|
75
|
-
line =~ /^$/ # Blank lines
|
76
|
-
}.reject { |line|
|
77
|
-
line =~ /^#{Uninteresting_status_flags.to_regexp_char_class}/
|
78
|
-
}.join
|
79
|
-
|
80
|
-
before_externals, *externals = input.split(/^Performing status on external item at.*$/)
|
81
|
-
|
82
|
-
before_externals ||= ''
|
83
|
-
before_externals = before_externals.strip.colorize_svn_status_lines + "\n" if before_externals != ""
|
75
|
+
class_extension do # These are actually class methods, but we have to do it this way so that the Subversion.extend(Subversion::Extensions) will also add these class methods to Subversion.
|
84
76
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
externals.reject { |line|
|
89
|
-
line =~ /^Performing status on external item at/
|
77
|
+
def status_lines_filter(input)
|
78
|
+
input = (input || "").reject { |line|
|
79
|
+
line =~ /^$/ # Blank lines
|
90
80
|
}.reject { |line|
|
81
|
+
line =~ /^#{Uninteresting_status_flags.to_regexp_char_class}/
|
82
|
+
}.join
|
83
|
+
|
84
|
+
before_externals, *externals = input.split(/^Performing status on external item at.*$/)
|
85
|
+
|
86
|
+
before_externals ||= ''
|
87
|
+
before_externals = before_externals.strip.colorize_svn_status_lines + "\n" if before_externals != ""
|
88
|
+
|
89
|
+
externals = externals.join.strip
|
90
|
+
externals =
|
91
|
+
'_'*40 + ' externals '.underline + '_'*40 + "\n" +
|
92
|
+
externals.reject { |line|
|
93
|
+
line =~ /^Performing status on external item at/
|
94
|
+
}.reject { |line|
|
95
|
+
line =~ /^$/ # Blank lines
|
96
|
+
}.join.strip.colorize_svn_status_lines + "\n" if externals != ""
|
97
|
+
|
98
|
+
before_externals +
|
99
|
+
externals
|
100
|
+
end
|
101
|
+
|
102
|
+
def update_lines_filter(input)
|
103
|
+
input.reject { |line|
|
91
104
|
line =~ /^$/ # Blank lines
|
92
|
-
}.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
end
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
Subversion::revision_properties(rev).map do |property|
|
137
|
-
"#{property.name.ljust(20)} = '#{property.value}'"
|
138
|
-
end.join("\n")
|
139
|
-
end
|
140
|
-
|
141
|
-
end
|
142
|
-
end
|
105
|
+
}.reject { |line|
|
106
|
+
line =~ /^Fetching external item into/
|
107
|
+
# Eventually we may want it to include this whole block, but only iff there is something updated for this external.
|
108
|
+
}.reject { |line|
|
109
|
+
line =~ /^External at revision/
|
110
|
+
}.join.colorize_svn_update_lines
|
111
|
+
# Also get rid of all but one "At revision _."?
|
112
|
+
end
|
113
|
+
|
114
|
+
def unadded_lines_filter(input)
|
115
|
+
input.select { |line|
|
116
|
+
line =~ /^\?/
|
117
|
+
}.join
|
118
|
+
end
|
119
|
+
def unadded_filter(input)
|
120
|
+
unadded_lines_filter(input).map { |line|
|
121
|
+
# Just keep the filename part
|
122
|
+
line =~ /^\?\s+(.+)/
|
123
|
+
$1
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def each_unadded(input)
|
128
|
+
unadded_filter(input).each { |line|
|
129
|
+
yield line
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
# This is just a wrapper for Subversion::diff that adds some color
|
134
|
+
def colorized_diff(*args)
|
135
|
+
Subversion::diff(*args).colorize_svn_diff.add_exit_code_error
|
136
|
+
end
|
137
|
+
|
138
|
+
# A wrapper for Subversion::revision_properties that formats it for display on srceen
|
139
|
+
def printable_revision_properties(rev)
|
140
|
+
Subversion::revision_properties(rev).map do |property|
|
141
|
+
"#{property.name.ljust(20)} = '#{property.value}'"
|
142
|
+
end.join("\n")
|
143
|
+
end
|
144
|
+
|
145
|
+
end # class_extension
|
146
|
+
|
147
|
+
end # module Extensions
|
148
|
+
end # module Subversion
|
@@ -1,15 +1,19 @@
|
|
1
|
+
# Tested by: ../../test/svn_command_test.rb
|
2
|
+
|
1
3
|
require 'rubygems'
|
2
4
|
|
3
5
|
gem 'facets', '>=1.8.51'
|
4
6
|
#require 'facets/more/command' # Not until they include my changes
|
5
|
-
require 'facets/core/string/margin'
|
6
7
|
require 'facets/core/kernel/require_local'
|
7
|
-
require 'facets/core/array/select' # select!
|
8
8
|
require 'facets/core/kernel/with' # returning
|
9
|
+
require 'facets/core/enumerable/every'
|
10
|
+
require 'facets/core/array/select' # select!
|
11
|
+
require 'facets/core/string/margin'
|
9
12
|
require 'facets/core/string/lines'
|
10
13
|
require 'facets/core/string/index_all'
|
11
14
|
require 'facets/core/string/to_re'
|
12
15
|
require 'facets/core/string/to_rx'
|
16
|
+
require 'facets/core/symbol/to_proc'
|
13
17
|
|
14
18
|
gem 'qualitysmith_extensions', '>=0.0.3'
|
15
19
|
require 'qualitysmith_extensions/enumerable/enum'
|
@@ -17,12 +21,14 @@ require 'qualitysmith_extensions/array/expand_ranges'
|
|
17
21
|
require 'qualitysmith_extensions/array/shell_escape'
|
18
22
|
require 'qualitysmith_extensions/file_test/binary_file'
|
19
23
|
require 'qualitysmith_extensions/console/command'
|
24
|
+
require 'qualitysmith_extensions/module/attribute_accessors'
|
25
|
+
#require 'qualitysmith_extensions/module/class_methods'
|
20
26
|
|
21
|
-
require 'extensions/symbol' # to_proc
|
22
27
|
require 'English'
|
23
28
|
require 'pp'
|
24
|
-
require 'termios'
|
25
29
|
require 'stringio'
|
30
|
+
gem 'termios'
|
31
|
+
require 'termios'
|
26
32
|
gem 'colored'
|
27
33
|
require 'colored' # Lets us do "a".white.bold instead of "\033[1ma\033[0m"
|
28
34
|
|
@@ -34,8 +40,9 @@ begin
|
|
34
40
|
# Set up termios so that it returns immediately when you press a key.
|
35
41
|
# (http://blog.rezra.com/articles/2005/12/05/single-character-input)
|
36
42
|
t = Termios.tcgetattr(STDIN)
|
43
|
+
save_terminal_attributes = t.dup
|
37
44
|
t.lflag &= ~Termios::ICANON
|
38
|
-
Termios.tcsetattr(STDIN,0,t)
|
45
|
+
Termios.tcsetattr(STDIN, 0, t)
|
39
46
|
rescue RuntimeError => exception # Necessary for automated testing.
|
40
47
|
if exception.message =~ /can't get terminal parameters/
|
41
48
|
puts 'Warning: Terminal not found.'
|
@@ -45,6 +52,20 @@ rescue RuntimeError => exception # Necessary for automated testing.
|
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
55
|
+
begin # Reset terminal_attributes
|
56
|
+
|
57
|
+
module Kernel
|
58
|
+
# Simply to allow us to override it
|
59
|
+
# Should be renamed to exit_status maybe since it returns a Process::Status
|
60
|
+
def exit_code
|
61
|
+
$?
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Object
|
66
|
+
def nonnil?; !nil?; end
|
67
|
+
end
|
68
|
+
|
48
69
|
class String
|
49
70
|
# Makes the first character bold and underlined. Makes the whole string of the given color.
|
50
71
|
# :todo: Move out to extensions/console/menu_item
|
@@ -56,8 +77,9 @@ class String
|
|
56
77
|
after = self[index+1..-1].send(color)
|
57
78
|
before.to_s + middle + after
|
58
79
|
end
|
80
|
+
# Extracted so that we can override it for tests. Otherwise it will have a NoMethodError because $? will be nil because it will not have actually executed any commands.
|
59
81
|
def add_exit_code_error
|
60
|
-
self << "Exited with error!".bold.red if
|
82
|
+
self << "Exited with error!".bold.red if !exit_code.success?
|
61
83
|
self
|
62
84
|
end
|
63
85
|
def relativize_path
|
@@ -78,33 +100,60 @@ def confirm(question, options = ['Yes', 'No'])
|
|
78
100
|
response
|
79
101
|
end
|
80
102
|
|
81
|
-
Subversion.extend(Subversion::Extensions)
|
103
|
+
#Subversion.extend(Subversion::Extensions)
|
104
|
+
module Subversion
|
105
|
+
include(Subversion::Extensions)
|
106
|
+
end
|
82
107
|
Subversion::color = true
|
83
108
|
|
84
109
|
module Subversion
|
85
|
-
# Tested by: ../../test/svn_command_test.rb
|
86
110
|
class SvnCommand < Console::Command
|
87
|
-
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Handle user preferences
|
115
|
+
module Subversion
|
116
|
+
class SvnCommand
|
117
|
+
@@user_preferences = {}
|
118
|
+
mattr_accessor :user_preferences
|
119
|
+
end
|
120
|
+
end
|
121
|
+
if File.exists?(user_preference_file = "#{ENV['HOME']}/.svn-command.yml")
|
122
|
+
Subversion::SvnCommand.user_preferences = YAML::load(IO.read(user_preference_file)) || {}
|
123
|
+
end
|
124
|
+
|
125
|
+
module Subversion
|
126
|
+
class SvnCommand
|
127
|
+
|
128
|
+
# Constants
|
129
|
+
C_standard_remote_command_options = {
|
88
130
|
[:__username] => 1,
|
89
131
|
[:__password] => 1,
|
90
132
|
[:__no_auth_cache] => 0,
|
91
133
|
[:__non_interactive] => 0,
|
92
134
|
[:__config_dir] => 1,
|
93
135
|
}
|
94
|
-
|
136
|
+
C_standard_commitable_command_options = {
|
137
|
+
[:_m, :__message] => 1,
|
138
|
+
[:_F, :__file] => 1,
|
139
|
+
[:__force_log] => 0,
|
140
|
+
[:__editor_cmd] => 1,
|
141
|
+
[:__encoding] => 1,
|
142
|
+
}
|
95
143
|
|
96
144
|
# This shouldn't be necessary. Console::Command should allow introspection. But until such time...
|
97
145
|
@@subcommand_list = [
|
98
146
|
'each_unadded',
|
99
147
|
'externals_items', 'externals_outline', 'externals_containers', 'edit_externals', 'externalize',
|
100
|
-
'ignore',
|
148
|
+
'ignore', 'edit_ignores',
|
101
149
|
'revisions',
|
102
150
|
'get_message', 'set_message', 'edit_message',
|
103
151
|
'view_commits',
|
104
152
|
'url',
|
105
|
-
'repository_root',
|
153
|
+
'repository_root', 'working_copy_root', 'repository_uuid',
|
106
154
|
'latest_revision',
|
107
|
-
'delete_svn'
|
155
|
+
'delete_svn',
|
156
|
+
'fix_out_of_date_commit_state'
|
108
157
|
]
|
109
158
|
mattr_reader :subcommand_list
|
110
159
|
|
@@ -186,8 +235,12 @@ module Subversion
|
|
186
235
|
# * Assume arity of 1. Then if it was really 0, it would pick up an extra arg that really wasn't supposed to be an arg for the *option*.
|
187
236
|
# Ideally, we wouldn't be using option_missing at all because all options would be listed in the respective subcommand module...but for subcommands handled through method_missing, we don't have that option.
|
188
237
|
|
189
|
-
|
190
|
-
|
238
|
+
# The args will look like this, for example:
|
239
|
+
# option_missing(-m, ["a multi-word message", "--something-else", "something else"])
|
240
|
+
# , so we need to be sure we wrap multi-word args in quotes as necessary. That's what the args.shell_escape does.
|
241
|
+
|
242
|
+
@passthrough_options << "#{option_name}" << args.shell_escape
|
243
|
+
@passthrough_options.flatten! # necessary now that we have args.shell_escape ?
|
191
244
|
|
192
245
|
return arity = args.size # All of 'em
|
193
246
|
end
|
@@ -220,19 +273,18 @@ module Subversion
|
|
220
273
|
[:_N, :__non_recursive] => 1,
|
221
274
|
[:__targets] => 1,
|
222
275
|
[:__no_unlock] => 0,
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
[:__editor_cmd] => 1,
|
227
|
-
[:__encoding] => 1,
|
228
|
-
}.merge(SvnCommand::standard_remote_command_options), self
|
276
|
+
}.
|
277
|
+
merge(SvnCommand::C_standard_remote_command_options).
|
278
|
+
merge(SvnCommand::C_standard_commitable_command_options), self
|
229
279
|
)
|
230
280
|
|
231
281
|
# Use this flag if you don't want a commit notification to be sent out.
|
232
282
|
def __skip_notification
|
233
283
|
@skip_notification = true
|
234
284
|
end
|
235
|
-
alias_method :__covert,
|
285
|
+
alias_method :__covert, :__skip_notification
|
286
|
+
alias_method :__minor_edit, :__skip_notification # Like in MediaWiki. If you're just fixing a typo or something, then most people probably don't want to hear about it.
|
287
|
+
alias_method :__minor, :__skip_notification
|
236
288
|
|
237
289
|
# Use this flag if you are about to commit some code for which you know the tests aren't or (probaby won't) pass.
|
238
290
|
# This *may* cause your continuous integration system to either skip tests for this revision or at least be a little more
|
@@ -248,28 +300,81 @@ module Subversion
|
|
248
300
|
end
|
249
301
|
alias_method :__expect_to_break_tests, :__broken
|
250
302
|
alias_method :__knowingly_committing_broken_code, :__broken
|
251
|
-
|
303
|
+
|
304
|
+
# Skips e-mail and marks reviewed=true
|
305
|
+
# Similar to the 'reviewed' command, which just marks reviewed=true
|
306
|
+
def __doesnt_need_review
|
307
|
+
@__doesnt_need_review = true
|
308
|
+
end
|
309
|
+
|
310
|
+
# :todo: svn doesn't allow you to commit changes to externals in the same transaction as your "main working copy", but we
|
311
|
+
# can provide the illusion that this is possible, by doing multiple commits, one for each working copy/external.
|
312
|
+
#
|
313
|
+
# When this option is used, the same commit message is used for all commits.
|
314
|
+
#
|
315
|
+
# Of course, this may not be what the user wants; the user may wish to specify a different commit message for the externals
|
316
|
+
# than for the "main working copy", in which case the user should not be using this option!
|
317
|
+
def __include_externals
|
318
|
+
@include_externals = true
|
319
|
+
end
|
320
|
+
|
321
|
+
# Causes blame/author for this commit/file to stay the same as previous revision of the file
|
322
|
+
# Useful to workaround bug where fixing indent and other inconsequential changes causes you to be displayed as the author if you do a blame, [hiding] the real author
|
323
|
+
def __shirk_blame
|
324
|
+
@shirk_blame = true
|
325
|
+
end
|
326
|
+
|
327
|
+
end #module Commit
|
328
|
+
|
252
329
|
def commit(*args)
|
253
|
-
|
330
|
+
directory = args.first || './' # We can only pass one path to .latest_revision and .repository_root, so we'll just arbitrarily choose the first path. They should all be paths within the same repository anyway, so it shouldn't matter.
|
331
|
+
if @broken || @skip_notification
|
332
|
+
latest_rev_before_commit = Subversion.latest_revision(directory)
|
333
|
+
repository_root = Subversion.repository_root(directory)
|
334
|
+
end
|
254
335
|
|
255
|
-
Subversion.
|
256
|
-
puts svn(:capture, "propset svn:skip_commit_notification_for_next_commit true --revprop -r #{latest_rev_before_commit}", :prepare_args => false)
|
336
|
+
Subversion.print_commands! do
|
337
|
+
puts svn(:capture, "propset svn:skip_commit_notification_for_next_commit true --revprop -r #{latest_rev_before_commit} #{repository_root}", :prepare_args => false)
|
257
338
|
end if @skip_notification
|
258
339
|
# :todo:
|
259
340
|
# Add some logic to automatically skip the commit e-mail if the size of the files to be committed exceeds a threshold of __ MB.
|
260
341
|
# (Performance idea: Only check the size of the files if svn st includes (bin)?)
|
261
342
|
|
343
|
+
# Have to use :system rather than :capture because they may not have specified a commit message, in which case it will open up an editor...
|
262
344
|
svn(:system, 'commit', *(['--force-log'] + args))
|
263
345
|
|
346
|
+
puts ''.add_exit_code_error
|
347
|
+
return if !exit_code.success?
|
348
|
+
|
264
349
|
# The following only works if we do :capture (`svn`), but that doesn't work so well (at all) if svn tries to open up an editor (vim),
|
265
350
|
# which is what happens if you don't specify a message.:
|
266
351
|
# puts output = svn(:capture, 'commit', *(['--force-log'] + args))
|
267
352
|
# just_committed = (matches = output.match(/Committed revision (\d+)\./)) && matches[1]
|
268
353
|
|
269
|
-
Subversion.
|
354
|
+
Subversion.print_commands! do
|
270
355
|
puts svn(:capture, "propset code:broken true --revprop -r #{latest_rev_before_commit + 1}", :prepare_args => false)
|
271
356
|
end if @broken
|
272
357
|
|
358
|
+
if @include_externals
|
359
|
+
#:todo:
|
360
|
+
#externals.each do |external|
|
361
|
+
#svn(:system, 'commit', *(['--force-log'] + args + external))
|
362
|
+
#end
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
# This should be disableable! ~/.svn-command ?
|
367
|
+
# http://svn.collab.net/repos/svn/trunk/doc/user/svn-best-practices.html:
|
368
|
+
# After every svn commit, your working copy has mixed revisions. The things you just committed are now at the HEAD revision, and everything else is at an older revision.
|
369
|
+
#puts "Whenever you commit something, strangely, your working copy becomes out of date (as you can observe if you run svn info and look at the revision number). This is a problem for svn log, and piston, to name two applications. So we will now update '#{(args.every + '/..').join(' ').white.bold}' just to make sure they're not out of date..."
|
370
|
+
print ''.bold # Clear the bold flag that svn annoyingly sets
|
371
|
+
working_copy_root = Subversion.working_copy_root(directory).to_s
|
372
|
+
#response = confirm("Do you want to update #{working_copy_root.bold} now? (Any key other than y to skip) ")
|
373
|
+
#if response == 'y'
|
374
|
+
puts "Updating #{working_copy_root}..."
|
375
|
+
#end
|
376
|
+
|
377
|
+
puts Subversion.update_lines_filter( Subversion.update(*args) )
|
273
378
|
end
|
274
379
|
|
275
380
|
# Ideas:
|
@@ -284,11 +389,11 @@ module Subversion
|
|
284
389
|
|
285
390
|
puts Subversion.export("#{dir}", "#{dir}.new"). # Exports (copies) the contents of working copy 'dir' (including your uncommitted changes, don't worry! ... and you'll get a chance to confirm before anything is deleted; but sometimes although it exports files that are scheduled for addition, they are no longer scheduled for addition in the new working copy, so you have to re-add them) to non-working-copy 'dir.new'
|
286
391
|
add_exit_code_error
|
287
|
-
return if
|
392
|
+
return if !exit_code.success?
|
288
393
|
|
289
394
|
system("mv #{dir} #{dir}.backup") # Just in case something goes ary
|
290
395
|
puts ''.add_exit_code_error
|
291
|
-
return if
|
396
|
+
return if !exit_code.success?
|
292
397
|
|
293
398
|
puts "Restoring #{dir}..."
|
294
399
|
Subversion.update dir # Restore the directory to a pristine state so we will no longer get that annoying error
|
@@ -301,21 +406,26 @@ module Subversion
|
|
301
406
|
system("cp -R #{dir}.new/. #{dir}/")
|
302
407
|
|
303
408
|
# Assure the user one more time
|
304
|
-
puts
|
409
|
+
puts Subversion.colorized_diff(dir)
|
410
|
+
|
411
|
+
puts "Please check the output of " + "svn st #{dir}.backup".blue.bold + " to check if any files were scheduled for addition. You will need to manually re-add these, as the export will have caused those files to lost their scheduling."
|
412
|
+
Subversion.print_commands! do
|
413
|
+
print Subversion.status_lines_filter( Subversion.status("#{dir}.backup") )
|
414
|
+
print Subversion.status_lines_filter( Subversion.status("#{dir}") )
|
415
|
+
end
|
305
416
|
|
306
417
|
# Actually commit
|
307
418
|
puts
|
308
419
|
response = confirm("Are you ready to try the commit again now?")
|
309
420
|
puts
|
310
421
|
if response == 'y'
|
311
|
-
puts "Great
|
312
|
-
#Subversion.commit dir
|
422
|
+
puts "Great! Go for it. (I'd do it for you but I don't know what commit command you were trying to execute when the problem occurred.)"
|
313
423
|
end
|
314
424
|
|
315
425
|
# Clean up
|
316
426
|
#puts
|
317
427
|
#response = confirm("Do you want to delete array.backup array.new now?")
|
318
|
-
puts "Don't forget to
|
428
|
+
puts "Don't forget to " + "rm -rf #{dir}.backup #{dir}.new".blue.bold + " when you are done!"
|
319
429
|
#rm_rf array.backup, array.new
|
320
430
|
puts
|
321
431
|
end
|
@@ -326,33 +436,42 @@ module Subversion
|
|
326
436
|
[:_r, :__revision] => 1, # :todo: support "{" DATE "}" format
|
327
437
|
[:__old] => 1,
|
328
438
|
[:__new] => 0,
|
329
|
-
[:_N, :__non_recursive] =>
|
439
|
+
#[:_N, :__non_recursive] => 0,
|
330
440
|
[:__diff_cmd] => 1,
|
331
441
|
[:_x, :__extensions] => 1, # :todo: should support any number of args??
|
332
442
|
[:__no_diff_deleted] => 0,
|
333
443
|
[:__notice_ancestry] => 1,
|
334
444
|
[:__force] => 1,
|
335
|
-
}.merge(SvnCommand::
|
445
|
+
}.merge(SvnCommand::C_standard_remote_command_options), self
|
336
446
|
)
|
447
|
+
|
448
|
+
def __non_recursive
|
449
|
+
@non_recursive = true
|
450
|
+
@passthrough_options << '--non-recursive'
|
451
|
+
end
|
452
|
+
alias_method :_N, :__non_recursive
|
453
|
+
|
337
454
|
def __ignore_externals
|
338
455
|
@ignore_externals = true
|
339
456
|
end
|
457
|
+
alias_method :_ie, :__ignore_externals
|
458
|
+
alias_method :_skip_externals, :__ignore_externals
|
340
459
|
end
|
341
460
|
|
342
461
|
def diff(*directories)
|
343
462
|
directories = ['./'] if directories.empty?
|
344
|
-
puts
|
463
|
+
puts Subversion.colorized_diff(*(prepare_args(directories)))
|
345
464
|
|
346
465
|
begin # Show diff for externals (if there *are* any and the user didn't tell us to ignore them)
|
347
466
|
output = StringIO.new
|
348
467
|
#paths = args.reject{|arg| arg =~ /^-/} || ['./']
|
349
468
|
directories.each do |path|
|
350
469
|
(Subversion.externals_items(path) || []).each do |item|
|
351
|
-
diff_output =
|
470
|
+
diff_output = Subversion.colorized_diff(item).strip
|
352
471
|
unless diff_output == ""
|
353
472
|
#output.puts '-'*100
|
354
473
|
#output.puts item.ljust(100, ' ').black_on_white.bold.underline
|
355
|
-
output.puts item.ljust(100).
|
474
|
+
output.puts item.ljust(100).yellow_on_red.bold
|
356
475
|
output.puts diff_output
|
357
476
|
end
|
358
477
|
end
|
@@ -363,7 +482,8 @@ module Subversion
|
|
363
482
|
puts " Diff of externals (**don't forget to commit these too!**):".ljust(100, ' ').yellow_on_red.bold.underline
|
364
483
|
puts output.string
|
365
484
|
end
|
366
|
-
end unless @ignore_externals
|
485
|
+
end unless @ignore_externals || @non_recursive
|
486
|
+
# :todo: or @non_recursive (but that's currently a passthrough option that doesn't set @non_recursive)
|
367
487
|
end
|
368
488
|
|
369
489
|
#-----------------------------------------------------------------------------------------------------------------------------
|
@@ -422,16 +542,56 @@ End
|
|
422
542
|
[:__incremental] => 0,
|
423
543
|
[:__xml] => 0,
|
424
544
|
[:__limit] => 1,
|
425
|
-
}.merge(SvnCommand::
|
545
|
+
}.merge(SvnCommand::C_standard_remote_command_options), self
|
426
546
|
)
|
427
547
|
end
|
428
548
|
def log(*args)
|
429
|
-
puts Subversion.log(
|
549
|
+
puts Subversion.log( prepare_args(args) )
|
430
550
|
#svn :exec, *args
|
431
551
|
end
|
432
552
|
|
433
553
|
# Ideas:
|
434
554
|
# Just pass a number (5) and it will be treated as --limit 5 (unless File.exists?('5'))
|
555
|
+
|
556
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
557
|
+
module Mkdir
|
558
|
+
Console::Command.pass_through({
|
559
|
+
[:_q, :__quiet] => 0,
|
560
|
+
}.
|
561
|
+
merge(SvnCommand::C_standard_remote_command_options).
|
562
|
+
merge(SvnCommand::C_standard_commitable_command_options), self
|
563
|
+
)
|
564
|
+
|
565
|
+
# Make parent directories as needed. (Like the --parents option of GNU mkdir.)
|
566
|
+
def __parents; @create_parents = true; end
|
567
|
+
alias_method :_p, :__parents
|
568
|
+
end
|
569
|
+
|
570
|
+
def mkdir(*directories)
|
571
|
+
if @create_parents
|
572
|
+
directories.each do |directory|
|
573
|
+
|
574
|
+
# :todo: change this so that it's guaranteed to have an exit condition; currently, can get into infinite loop
|
575
|
+
loop do
|
576
|
+
puts "Creating '#{directory}'"
|
577
|
+
FileUtils.mkdir_p directory # Create it if it doesn't already exist
|
578
|
+
if Subversion.under_version_control?(File.dirname(directory))
|
579
|
+
# Yay, we found a working copy. Now we can issue an add command, from that directory, which will recursively add the
|
580
|
+
# (non-working copy) directories we've been creating along the way.
|
581
|
+
|
582
|
+
#puts Subversion.add prepare_args([directory])
|
583
|
+
svn :system, 'add', *directory
|
584
|
+
break
|
585
|
+
else
|
586
|
+
directory = File.dirname(directory)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
else
|
591
|
+
# Preserve default behavior.
|
592
|
+
svn :system, 'mkdir', *directories
|
593
|
+
end
|
594
|
+
end
|
435
595
|
|
436
596
|
#-----------------------------------------------------------------------------------------------------------------------------
|
437
597
|
module Move
|
@@ -439,13 +599,20 @@ End
|
|
439
599
|
[:_r, :__revision] => 1, # :todo: support "{" DATE "}" format
|
440
600
|
[:_q, :__quiet] => 0,
|
441
601
|
[:__force] => 0,
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
[:__editor_cmd] => 1,
|
446
|
-
[:__encoding] => 1,
|
447
|
-
}.merge(SvnCommand::standard_remote_command_options), self
|
602
|
+
}.
|
603
|
+
merge(SvnCommand::C_standard_remote_command_options).
|
604
|
+
merge(SvnCommand::C_standard_commitable_command_options), self
|
448
605
|
)
|
606
|
+
|
607
|
+
# If the directory specified by the destination path does not exist, it will `svn mkdir --parents` the directory for you to
|
608
|
+
# save you the trouble (and to save you from getting an error message!).
|
609
|
+
#
|
610
|
+
# For example, if you try to move file_name to dir1/dir2/new_file_name and dir1/dir2 is not under version control, then it
|
611
|
+
# will effectively do these commands:
|
612
|
+
# svn mkdir --parents dir1/dir2
|
613
|
+
# svn mv a dir1/dir2/new_file_name # The command you were originally trying to do
|
614
|
+
def __parents; @create_parents = true; end
|
615
|
+
alias_method :_p, :__parents
|
449
616
|
end
|
450
617
|
|
451
618
|
# Unlike the built-in move, this one lets you list multiple source files
|
@@ -453,19 +620,64 @@ End
|
|
453
620
|
# or
|
454
621
|
# Source Destination
|
455
622
|
def move(*args)
|
456
|
-
|
457
|
-
|
623
|
+
destination = args.pop
|
624
|
+
|
625
|
+
# If the last character is a '/', then they obviously expect the destination to be a *directory*. Yet when I do this:
|
626
|
+
# svn mv a b/
|
627
|
+
# and b doesn't exist,
|
628
|
+
# it moves a (a file) to b as a file, rather than creating directory b/ and moving a to b/a.
|
629
|
+
# I find this default behavior less than intuitive, so I have "fixed" it here...
|
630
|
+
# So instead of seeing this:
|
631
|
+
# A b
|
632
|
+
# D a
|
633
|
+
# You should see this:
|
634
|
+
# A b
|
635
|
+
# A b/a
|
636
|
+
# D a
|
637
|
+
if destination[-1..-1] == '/'
|
638
|
+
if !File.exist?(destination[0..-2])
|
639
|
+
puts "Notice: It appears that the '" + destination.bold + "' directory doesn't exist. Would you like to create it now? Good..."
|
640
|
+
self.mkdir destination # @create_parents flag will be reused there
|
641
|
+
elsif !File.directory?(destination[0..-2])
|
642
|
+
puts "Error".red.bold + ": It appears that '" + destination.bold + "' already exists but is not actually a directory. " +
|
643
|
+
"The " + 'destination'.bold + " must either be the path to a " + 'file'.underline + " that does " + 'not'.underline + " yet exist or the path to a " + 'directory'.underline + " (which may or may not yet exist)."
|
644
|
+
return
|
645
|
+
end
|
646
|
+
end
|
647
|
+
|
648
|
+
if @create_parents and !Subversion.under_version_control?(destination_dir = File.dirname(destination))
|
649
|
+
puts "Creating parent directory '#{destination_dir}'..."
|
650
|
+
self.mkdir destination_dir # @create_parents flag will be reused there
|
651
|
+
end
|
652
|
+
if args.length >= 2
|
458
653
|
sources = args
|
459
654
|
|
460
655
|
sources.each do |source|
|
461
656
|
puts filtered_svn('move', source, destination)
|
462
657
|
end
|
463
658
|
else
|
464
|
-
svn :exec, 'move', *args
|
659
|
+
svn :exec, 'move', *(args + [destination])
|
465
660
|
end
|
466
661
|
end
|
467
662
|
alias_subcommand :mv => :move
|
468
663
|
|
664
|
+
#-----------------------------------------------------------------------------------------------------------------------------
|
665
|
+
module Import
|
666
|
+
Console::Command.pass_through({
|
667
|
+
[:_N, :__non_recursive] => 0,
|
668
|
+
[:_q, :__quiet] => 0,
|
669
|
+
[:__auto_props] => 0,
|
670
|
+
[:__no_auto_props] => 0,
|
671
|
+
}.
|
672
|
+
merge(SvnCommand::C_standard_remote_command_options).
|
673
|
+
merge(SvnCommand::C_standard_commitable_command_options), self
|
674
|
+
)
|
675
|
+
end
|
676
|
+
def import(*args)
|
677
|
+
p args
|
678
|
+
svn :exec, 'import', *(args)
|
679
|
+
end
|
680
|
+
|
469
681
|
#-----------------------------------------------------------------------------------------------------------------------------
|
470
682
|
module Status
|
471
683
|
Console::Command.pass_through({
|
@@ -477,14 +689,11 @@ End
|
|
477
689
|
[:__incremental] => 0,
|
478
690
|
[:__xml] => 0,
|
479
691
|
[:__ignore_externals] => 0,
|
480
|
-
}.merge(SvnCommand::
|
692
|
+
}.merge(SvnCommand::C_standard_remote_command_options), self
|
481
693
|
)
|
482
694
|
end
|
483
695
|
def status(*args)
|
484
|
-
|
485
|
-
#puts "#{self}.@passthrough_options == #{@passthrough_options.inspect}"
|
486
|
-
#print Subversion::Extensions.status_lines_filter( Subversion.status(*(@passthrough_options + args)) )
|
487
|
-
print Subversion::Extensions.status_lines_filter( Subversion.status(*(@passthrough_options + args)) )
|
696
|
+
print Subversion.status_lines_filter( Subversion.status(*(prepare_args(args))) )
|
488
697
|
end
|
489
698
|
|
490
699
|
#-----------------------------------------------------------------------------------------------------------------------------
|
@@ -494,12 +703,24 @@ End
|
|
494
703
|
[:_N, :__non_recursive] => 0,
|
495
704
|
[:_q, :__quiet] => 0,
|
496
705
|
[:__diff3_cmd] => 1,
|
497
|
-
[:__ignore_externals] => 0,
|
498
|
-
}.merge(SvnCommand::
|
706
|
+
#[:__ignore_externals] => 0,
|
707
|
+
}.merge(SvnCommand::C_standard_remote_command_options), self
|
499
708
|
)
|
709
|
+
|
710
|
+
def __ignore_externals; @ignore_externals = true; end
|
711
|
+
def __include_externals; @ignore_externals = false; end
|
712
|
+
def __with_externals; @ignore_externals = false; end
|
713
|
+
|
714
|
+
def ignore_externals?
|
715
|
+
@ignore_externals.nonnil? ?
|
716
|
+
@ignore_externals :
|
717
|
+
(user_preferences['update'] && user_preferences['update']['ignore_externals'])
|
718
|
+
end
|
500
719
|
end
|
720
|
+
|
501
721
|
def update(*args)
|
502
|
-
|
722
|
+
@passthrough_options << '--non-recursive' if ignore_externals?
|
723
|
+
puts Subversion.update_lines_filter( Subversion.update(*prepare_args(args)) )
|
503
724
|
end
|
504
725
|
|
505
726
|
|
@@ -520,6 +741,25 @@ End
|
|
520
741
|
end
|
521
742
|
|
522
743
|
#-----------------------------------------------------------------------------------------------------------------------------
|
744
|
+
|
745
|
+
def under_version_control(*args)
|
746
|
+
puts Subversion.under_version_control?(*args)
|
747
|
+
end
|
748
|
+
alias_subcommand :under_version_control? => :under_version_control
|
749
|
+
|
750
|
+
# Returns root/base *path* for a working copy
|
751
|
+
def working_copy_root(*args)
|
752
|
+
puts Subversion.working_copy_root(*args)
|
753
|
+
end
|
754
|
+
alias_subcommand :root => :working_copy_root
|
755
|
+
|
756
|
+
# Returns the UUID for a working copy/URL
|
757
|
+
def repository_uuid(*args)
|
758
|
+
puts Subversion.repository_uuid(*args)
|
759
|
+
end
|
760
|
+
alias_subcommand :uuid => :repository_uuid
|
761
|
+
|
762
|
+
# Returns root repository *URL* for a working copy
|
523
763
|
def repository_root(*args)
|
524
764
|
puts Subversion.repository_root(*args)
|
525
765
|
end
|
@@ -595,7 +835,7 @@ End
|
|
595
835
|
catch :exit do
|
596
836
|
|
597
837
|
$ignore_dry_run_option = true
|
598
|
-
Subversion
|
838
|
+
Subversion.each_unadded( Subversion.status(*args) ) do |file|
|
599
839
|
$ignore_dry_run_option = false
|
600
840
|
begin
|
601
841
|
puts( ('-'*100).green )
|
@@ -628,6 +868,7 @@ End
|
|
628
868
|
"Add".menu_item(:green) + ", " +
|
629
869
|
"Delete".menu_item(:red) + ", " +
|
630
870
|
"add to " + "svn:".yellow + "Ignore".menu_item(:yellow) + " property, " +
|
871
|
+
"ignore ".yellow + "Contents".menu_item(:yellow) + " of directory, " +
|
631
872
|
"or " + "any other key".white.bold + " to do nothing > "
|
632
873
|
)
|
633
874
|
response = ""
|
@@ -761,11 +1002,10 @@ End
|
|
761
1002
|
end
|
762
1003
|
Subversion.externals_containers(directory).each do |external|
|
763
1004
|
puts external.to_s
|
764
|
-
command = "
|
765
|
-
#puts command
|
1005
|
+
command = "propedit svn:externals #{external.container_dir}"
|
766
1006
|
begin
|
767
1007
|
response = confirm("Do you want to edit svn:externals for this directory?".black_on_white)
|
768
|
-
system
|
1008
|
+
svn :system, command if response == 'y'
|
769
1009
|
rescue Interrupt
|
770
1010
|
puts "\nGoodbye!"
|
771
1011
|
throw :exit
|
@@ -775,7 +1015,8 @@ End
|
|
775
1015
|
end
|
776
1016
|
puts 'Done'
|
777
1017
|
else
|
778
|
-
system "#{Subversion.executable} propedit svn:externals #{directory}"
|
1018
|
+
#system "#{Subversion.executable} propedit svn:externals #{directory}"
|
1019
|
+
svn :system, "propedit svn:externals #{directory}"
|
779
1020
|
end
|
780
1021
|
end # catch :exit
|
781
1022
|
end
|
@@ -811,6 +1052,14 @@ End
|
|
811
1052
|
Subversion.ignore(file)
|
812
1053
|
end
|
813
1054
|
|
1055
|
+
# Example:
|
1056
|
+
# svn edit_ignores tmp/sessions/
|
1057
|
+
def edit_ignores(directory = './')
|
1058
|
+
#puts Subversion.get_property("ignore", directory)
|
1059
|
+
# If it's empty, ask them if they want to edit it anyway??
|
1060
|
+
|
1061
|
+
svn :system, "propedit svn:ignore #{directory}"
|
1062
|
+
end
|
814
1063
|
|
815
1064
|
#-----------------------------------------------------------------------------------------------------------------------------
|
816
1065
|
# Commit message retrieving/editing
|
@@ -868,7 +1117,7 @@ End
|
|
868
1117
|
args = ['propedit', '--revprop', property_name, directory]
|
869
1118
|
rev = @revision ? @revision : 'head'
|
870
1119
|
args.concat ['-r', rev]
|
871
|
-
Subversion.
|
1120
|
+
Subversion.print_commands! do
|
872
1121
|
svn :system, *args
|
873
1122
|
end
|
874
1123
|
|
@@ -884,7 +1133,7 @@ End
|
|
884
1133
|
response = confirm("Are you sure you want to delete property #{property_name}".red.bold + "'? ")
|
885
1134
|
puts
|
886
1135
|
if response == 'y'
|
887
|
-
Subversion.
|
1136
|
+
Subversion.print_commands! do
|
888
1137
|
Subversion::delete_revision_property(property_name, rev)
|
889
1138
|
end
|
890
1139
|
end
|
@@ -906,9 +1155,14 @@ End
|
|
906
1155
|
|
907
1156
|
#-----------------------------------------------------------------------------------------------------------------------------
|
908
1157
|
# Cause a working copy to cease being a working copy
|
909
|
-
def delete_svn
|
910
|
-
|
911
|
-
system(
|
1158
|
+
def delete_svn(directory = './')
|
1159
|
+
puts "If you continue, all of the following directories/files will be deleted:"
|
1160
|
+
system("find #{directory} -name .svn | xargs -n1 echo")
|
1161
|
+
response = confirm("Do you wish to continue?")
|
1162
|
+
puts
|
1163
|
+
if response == 'y'
|
1164
|
+
system("find #{directory} -name .svn | xargs -n1 rm -r")
|
1165
|
+
end
|
912
1166
|
end
|
913
1167
|
|
914
1168
|
#-----------------------------------------------------------------------------------------------------------------------------
|
@@ -1004,12 +1258,13 @@ End
|
|
1004
1258
|
|
1005
1259
|
# Display the menu
|
1006
1260
|
print(
|
1261
|
+
"r#{rev}".magenta.on_blue.bold + ': ' +
|
1007
1262
|
'View this changeset'.menu_item(:cyan) + ', ' +
|
1008
1263
|
'Diff against specific revision'.menu_item(:cyan, 'D') + ', ' +
|
1009
1264
|
'Grep the changeset'.menu_item(:cyan, 'G') + ', ' +
|
1010
1265
|
'List or '.menu_item(:magenta, 'L') + '' +
|
1011
1266
|
'Edit revision properties'.menu_item(:magenta, 'E') + ', ' +
|
1012
|
-
'svn Cat all files
|
1267
|
+
'svn Cat all files'.menu_item(:cyan, 'C') + ', ' +
|
1013
1268
|
'grep the cat'.menu_item(:cyan, 'a') + ', ' + "\n " +
|
1014
1269
|
'mark as Reviewed'.menu_item(:green, 'R') + ', ' +
|
1015
1270
|
'edit log Message'.menu_item(:yellow, 'M') + ', ' +
|
@@ -1037,20 +1292,23 @@ End
|
|
1037
1292
|
end
|
1038
1293
|
|
1039
1294
|
case response
|
1040
|
-
when 'v' #
|
1295
|
+
when 'v' # View this changeset
|
1041
1296
|
revs_to_compare = [other_rev, rev]
|
1042
1297
|
puts "\n"*10
|
1043
1298
|
puts((' '*100).green.underline)
|
1044
1299
|
print "Diffing #{revs_to_compare.min}:#{revs_to_compare.max}... ".bold
|
1045
1300
|
puts
|
1046
1301
|
#Subversion.repository_root
|
1047
|
-
|
1302
|
+
Subversion.print_commands! do
|
1303
|
+
SvnCommand.execute("diff #{directory} --ignore-externals -r #{revs_to_compare.min}:#{revs_to_compare.max}")
|
1304
|
+
end
|
1305
|
+
show_revision_again = false
|
1048
1306
|
|
1049
1307
|
when 'g' # Grep the changeset
|
1050
1308
|
# :todo; make it accept regexpes like /like.*this/im so you can make it case insensitive or multi-line
|
1051
1309
|
revs_to_compare = [other_rev, rev]
|
1052
1310
|
puts
|
1053
|
-
print 'Grep for'.bold + ' (Regular expressions ' + 'like.*this'.bold.blue + '
|
1311
|
+
print 'Grep for'.bold + ' (Case sensitive; Regular expressions ' + 'like.*this'.bold.blue + ' allowed, but not ' + '/like.*this/im'.bold.blue + ') (backspace not currently supported): '
|
1054
1312
|
search_pattern = $stdin.gets.chomp.to_rx
|
1055
1313
|
puts((' '*100).green.underline)
|
1056
1314
|
puts "Searching `svn diff #{revs_to_compare.min}:#{revs_to_compare.max}` for #{search_pattern.to_s}... ".bold
|
@@ -1076,18 +1334,18 @@ End
|
|
1076
1334
|
|
1077
1335
|
when 'l' # List revision properties
|
1078
1336
|
puts
|
1079
|
-
puts Subversion::
|
1337
|
+
puts Subversion::printable_revision_properties(rev)
|
1080
1338
|
show_revision_again = false
|
1081
1339
|
|
1082
1340
|
when 'e' # Edit revision property
|
1083
1341
|
puts
|
1084
|
-
puts Subversion::
|
1342
|
+
puts Subversion::printable_revision_properties(rev)
|
1085
1343
|
puts "Warning: These properties are *not* under version control! Try not to permanently destroy anything *too* important...".red.bold
|
1086
1344
|
puts "Note: If you want to *delete* a property, simply set its value to '' and it will be deleted (propdel) for you."
|
1087
1345
|
print 'Which property would you like to edit'.bold + ' (backspace not currently supported)? '
|
1088
1346
|
property_name = $stdin.gets.chomp
|
1089
1347
|
unless property_name == ''
|
1090
|
-
Subversion.
|
1348
|
+
Subversion.print_commands! do
|
1091
1349
|
@revision = rev
|
1092
1350
|
edit_revision_property(property_name, directory)
|
1093
1351
|
end
|
@@ -1110,16 +1368,18 @@ End
|
|
1110
1368
|
# how to retrieve that (except by poking around in your ~/.subversion/ directory, but
|
1111
1369
|
# that seems kind of rude...).
|
1112
1370
|
puts "Marking as reviewed by '#{your_name}'..."
|
1113
|
-
Subversion.
|
1371
|
+
Subversion.print_commands! do
|
1114
1372
|
puts svn(:capture, "propset code:reviewed '#{your_name}' --revprop -r #{rev}", :prepare_args => false)
|
1115
1373
|
# :todo: Maybe *append* to code:reviewed (,-delimited) rather than overwriting it?, in case there is a policy of requiring 2 reviewers or something
|
1116
1374
|
end
|
1375
|
+
show_revision_again = false
|
1117
1376
|
|
1118
1377
|
when 'm' # Edit log message
|
1119
1378
|
puts
|
1120
|
-
Subversion.
|
1379
|
+
Subversion.print_commands! do
|
1121
1380
|
SvnCommand.execute("edit_message -r #{rev}")
|
1122
1381
|
end
|
1382
|
+
show_revision_again = false
|
1123
1383
|
|
1124
1384
|
when "\e[A" # Up
|
1125
1385
|
i = revision_ids.index(rev)
|
@@ -1192,6 +1452,7 @@ End
|
|
1192
1452
|
svn(:capture, *args).add_exit_code_error
|
1193
1453
|
end
|
1194
1454
|
|
1455
|
+
# Removes nil elements, converts to strings, and adds any pass-through args that may have been provided.
|
1195
1456
|
def prepare_args(args)
|
1196
1457
|
args.compact! # nil elements spell trouble
|
1197
1458
|
args.map!(&:to_s) # shell_escape doesn't like Fixnums either
|
@@ -1203,3 +1464,8 @@ End
|
|
1203
1464
|
end
|
1204
1465
|
end
|
1205
1466
|
end
|
1467
|
+
|
1468
|
+
ensure
|
1469
|
+
# Set terminal_attributes back to how we found them...
|
1470
|
+
Termios.tcsetattr(STDIN, 0, save_terminal_attributes)
|
1471
|
+
end
|