svn-command 0.2.7 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|