twig 1.6 → 1.7
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.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/HISTORY.md +27 -0
- data/README.md +41 -22
- data/bin/twig +12 -4
- data/bin/twig-checkout-child +56 -25
- data/bin/twig-checkout-parent +54 -26
- data/bin/twig-create-branch +40 -15
- data/bin/twig-diff +44 -25
- data/bin/twig-gh-open +43 -17
- data/bin/twig-gh-open-issue +51 -23
- data/bin/twig-gh-update +46 -20
- data/bin/twig-help +49 -5
- data/bin/twig-init +46 -13
- data/bin/twig-init-completion +46 -19
- data/bin/twig-init-completion-bash +50 -25
- data/bin/twig-init-config +77 -0
- data/bin/twig-rebase +85 -33
- data/config/twigconfig +47 -0
- data/lib/twig.rb +16 -10
- data/lib/twig/branch.rb +19 -12
- data/lib/twig/cli.rb +118 -183
- data/lib/twig/cli/help.rb +174 -0
- data/lib/twig/commit_time.rb +69 -14
- data/lib/twig/display.rb +19 -6
- data/lib/twig/github.rb +10 -8
- data/lib/twig/options.rb +22 -14
- data/lib/twig/subcommands.rb +13 -1
- data/lib/twig/system.rb +0 -2
- data/lib/twig/util.rb +0 -2
- data/lib/twig/version.rb +1 -1
- data/spec/spec_helper.rb +4 -3
- data/spec/twig/branch_spec.rb +100 -13
- data/spec/twig/cli/help_spec.rb +187 -0
- data/spec/twig/cli_spec.rb +34 -189
- data/spec/twig/commit_time_spec.rb +185 -16
- data/spec/twig/display_spec.rb +69 -48
- data/spec/twig/github_spec.rb +29 -15
- data/spec/twig/options_spec.rb +42 -13
- data/spec/twig/subcommands_spec.rb +35 -2
- data/spec/twig/system_spec.rb +5 -6
- data/spec/twig/util_spec.rb +20 -20
- data/spec/twig_spec.rb +21 -6
- data/twig.gemspec +14 -16
- metadata +23 -27
data/lib/twig/branch.rb
CHANGED
@@ -2,14 +2,14 @@ class Twig
|
|
2
2
|
|
3
3
|
# Represents a Git branch.
|
4
4
|
class Branch
|
5
|
-
|
6
|
-
EMPTY_PROPERTY_NAME_ERROR = 'Branch property names cannot be empty strings.'
|
5
|
+
PARENT_PROPERTY = 'diff-branch'
|
7
6
|
PROPERTY_NAME_FROM_GIT_CONFIG = /^branch\.[^.]+\.([^=]+)=.*$/
|
8
7
|
RESERVED_BRANCH_PROPERTY_NAMES = %w[branch merge property rebase remote]
|
9
8
|
|
10
9
|
class EmptyPropertyNameError < ArgumentError
|
10
|
+
DEFAULT_MESSAGE = 'Branch property names cannot be empty strings.'
|
11
11
|
def initialize(message = nil)
|
12
|
-
message ||=
|
12
|
+
message ||= DEFAULT_MESSAGE
|
13
13
|
super
|
14
14
|
end
|
15
15
|
end
|
@@ -24,9 +24,9 @@ class Twig
|
|
24
24
|
split("\n")
|
25
25
|
|
26
26
|
branch_tuples.inject([]) do |result, branch_tuple|
|
27
|
-
name, time_string
|
27
|
+
name, time_string = branch_tuple.split(REF_FORMAT_SEPARATOR)
|
28
28
|
time = Time.parse(time_string)
|
29
|
-
commit_time = Twig::CommitTime.new(time
|
29
|
+
commit_time = Twig::CommitTime.new(time)
|
30
30
|
branch = Branch.new(name, :last_commit_time => commit_time)
|
31
31
|
result << branch
|
32
32
|
end
|
@@ -34,7 +34,7 @@ class Twig
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.all_branch_names
|
37
|
-
@_all_branch_names ||=
|
37
|
+
@_all_branch_names ||= all_branches.map { |branch| branch.name }
|
38
38
|
end
|
39
39
|
|
40
40
|
def self.all_property_names
|
@@ -59,6 +59,10 @@ class Twig
|
|
59
59
|
raise EmptyPropertyNameError if property_name.empty?
|
60
60
|
end
|
61
61
|
|
62
|
+
def self.shellescape_property_value(property_value)
|
63
|
+
Shellwords.escape(property_value).gsub('\\ ', ' ')
|
64
|
+
end
|
65
|
+
|
62
66
|
def initialize(name, attrs = {})
|
63
67
|
self.name = name
|
64
68
|
raise ArgumentError, '`name` is required' if name.empty?
|
@@ -76,6 +80,10 @@ class Twig
|
|
76
80
|
}
|
77
81
|
end
|
78
82
|
|
83
|
+
def parent_name
|
84
|
+
get_property(PARENT_PROPERTY)
|
85
|
+
end
|
86
|
+
|
79
87
|
def sanitize_property(property_name)
|
80
88
|
property_name.gsub(/[ _]+/, '')
|
81
89
|
end
|
@@ -92,7 +100,7 @@ class Twig
|
|
92
100
|
return {} if property_names.empty?
|
93
101
|
|
94
102
|
property_names_regexp = escaped_property_names(property_names).join('|')
|
95
|
-
git_config_regexp = "branch\.#{name}\.(#{ property_names_regexp })$"
|
103
|
+
git_config_regexp = "branch\.#{Shellwords.escape(name)}\.(#{ property_names_regexp })$"
|
96
104
|
cmd = %{git config --get-regexp "#{git_config_regexp}"}
|
97
105
|
|
98
106
|
git_result = Twig.run(cmd) || ''
|
@@ -114,7 +122,6 @@ class Twig
|
|
114
122
|
properties.merge(property_name => property_value)
|
115
123
|
end
|
116
124
|
end
|
117
|
-
|
118
125
|
end
|
119
126
|
|
120
127
|
def get_property(property_name)
|
@@ -134,8 +141,9 @@ class Twig
|
|
134
141
|
raise ArgumentError,
|
135
142
|
%{Can't set a branch property to an empty string.}
|
136
143
|
else
|
137
|
-
git_config = "branch.#{name}.#{property_name}"
|
138
|
-
|
144
|
+
git_config = "branch.#{name.shellescape}.#{property_name}"
|
145
|
+
escaped_value = Branch.shellescape_property_value(value)
|
146
|
+
Twig.run(%{git config #{git_config} "#{escaped_value}"})
|
139
147
|
result_body = %{property "#{property_name}" as "#{value}" for branch "#{name}".}
|
140
148
|
if $?.success?
|
141
149
|
"Saved #{result_body}"
|
@@ -152,7 +160,7 @@ class Twig
|
|
152
160
|
value = get_property(property_name)
|
153
161
|
|
154
162
|
if value
|
155
|
-
git_config = "branch.#{name}.#{property_name}"
|
163
|
+
git_config = "branch.#{name.shellescape}.#{property_name}"
|
156
164
|
Twig.run(%{git config --unset #{git_config}})
|
157
165
|
%{Removed property "#{property_name}" for branch "#{name}".}
|
158
166
|
else
|
@@ -160,6 +168,5 @@ class Twig
|
|
160
168
|
%{The branch "#{name}" does not have the property "#{property_name}".}
|
161
169
|
end
|
162
170
|
end
|
163
|
-
|
164
171
|
end
|
165
172
|
end
|
data/lib/twig/cli.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'rbconfig'
|
3
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'cli', 'help'))
|
3
4
|
|
4
5
|
class Twig
|
6
|
+
# Handles raw input from the command-line interface.
|
5
7
|
module Cli
|
6
|
-
|
7
8
|
def self.prompt_with_choices(prompt, choices)
|
8
9
|
# Prints the given string `prompt` and the array `choices` numbered, and
|
9
10
|
# prompts the user to enter a number. Returns the matching value, or nil
|
10
11
|
# if the user input is invalid.
|
11
12
|
|
12
|
-
if choices.size < 2
|
13
|
-
raise ArgumentError, 'At least two choices required'
|
14
|
-
end
|
13
|
+
raise ArgumentError, 'At least two choices required' if choices.size < 2
|
15
14
|
|
16
15
|
puts prompt
|
17
16
|
choices.each_with_index do |choice, index|
|
@@ -23,106 +22,11 @@ class Twig
|
|
23
22
|
choices[input - 1]
|
24
23
|
end
|
25
24
|
|
26
|
-
def help_intro
|
27
|
-
version_string = "Twig v#{Twig::VERSION}"
|
28
|
-
|
29
|
-
intro = help_paragraph(%{
|
30
|
-
Twig is your personal Git branch assistant. It's a command-line tool for
|
31
|
-
listing your most recent branches, and for remembering branch details
|
32
|
-
for you, like issue tracker ids and todos. It also supports subcommands,
|
33
|
-
like automatically fetching statuses from your issue tracking system.
|
34
|
-
})
|
35
|
-
|
36
|
-
intro = <<-BANNER.gsub(/^[ ]+/, '')
|
37
|
-
|
38
|
-
#{version_string}
|
39
|
-
#{'-' * version_string.size}
|
40
|
-
|
41
|
-
#{intro}
|
42
|
-
|
43
|
-
#{Twig::HOMEPAGE}
|
44
|
-
BANNER
|
45
|
-
|
46
|
-
intro + ' ' # Force extra blank line
|
47
|
-
end
|
48
|
-
|
49
|
-
def help_separator(option_parser, text, options = {})
|
50
|
-
options[:trailing] ||= "\n\n"
|
51
|
-
option_parser.separator "\n#{text}#{options[:trailing]}"
|
52
|
-
end
|
53
|
-
|
54
|
-
def help_description(text, options = {})
|
55
|
-
width = options[:width] || 40
|
56
|
-
words = text.gsub(/\n?\s+/, ' ').strip.split(' ')
|
57
|
-
lines = []
|
58
|
-
|
59
|
-
# Split words into lines
|
60
|
-
while words.any?
|
61
|
-
current_word = words.shift
|
62
|
-
current_word_size = unformat_string(current_word).size
|
63
|
-
last_line = lines.last
|
64
|
-
last_line_size = last_line && unformat_string(last_line).size
|
65
|
-
|
66
|
-
if last_line_size && (last_line_size + current_word_size + 1 <= width)
|
67
|
-
last_line << ' ' << current_word
|
68
|
-
elsif current_word_size >= width
|
69
|
-
lines << current_word[0...width]
|
70
|
-
words.unshift(current_word[width..-1])
|
71
|
-
else
|
72
|
-
lines << current_word
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
lines << ' ' if options[:add_separator]
|
77
|
-
lines
|
78
|
-
end
|
79
|
-
|
80
|
-
def help_description_for_custom_property(option_parser, desc_lines, options = {})
|
81
|
-
options[:trailing] ||= "\n"
|
82
|
-
indent = ' '
|
83
|
-
left_column_width = 29
|
84
|
-
|
85
|
-
help_desc = desc_lines.inject('') do |desc, (left_column, right_column)|
|
86
|
-
desc + indent +
|
87
|
-
sprintf("%-#{left_column_width}s", left_column) + right_column + "\n"
|
88
|
-
end
|
89
|
-
|
90
|
-
help_separator(option_parser, help_desc, :trailing => options[:trailing])
|
91
|
-
end
|
92
|
-
|
93
|
-
def help_paragraph(text)
|
94
|
-
help_description(text, :width => 80).join("\n")
|
95
|
-
end
|
96
|
-
|
97
|
-
def help_line_for_custom_property?(line)
|
98
|
-
is_custom_property_except = (
|
99
|
-
line.include?('--except-') &&
|
100
|
-
!line.include?('--except-branch') &&
|
101
|
-
!line.include?('--except-property') &&
|
102
|
-
!line.include?('--except-PROPERTY')
|
103
|
-
)
|
104
|
-
is_custom_property_only = (
|
105
|
-
line.include?('--only-') &&
|
106
|
-
!line.include?('--only-branch') &&
|
107
|
-
!line.include?('--only-property') &&
|
108
|
-
!line.include?('--only-PROPERTY')
|
109
|
-
)
|
110
|
-
is_custom_property_width = (
|
111
|
-
line =~ /--.+-width/ &&
|
112
|
-
!line.include?('--branch-width') &&
|
113
|
-
!line.include?('--PROPERTY-width')
|
114
|
-
)
|
115
|
-
|
116
|
-
is_custom_property_except ||
|
117
|
-
is_custom_property_only ||
|
118
|
-
is_custom_property_width
|
119
|
-
end
|
120
|
-
|
121
25
|
def run_pager
|
122
26
|
# Starts a pager so that all following STDOUT output is paginated.
|
123
27
|
# Based on: http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby
|
124
28
|
|
125
|
-
return if Twig::System.windows? || !$stdout.tty?
|
29
|
+
return if Twig::System.windows? || !$stdout.tty? || !Kernel.respond_to?(:fork)
|
126
30
|
|
127
31
|
read_io, write_io = IO.pipe
|
128
32
|
|
@@ -152,28 +56,28 @@ class Twig
|
|
152
56
|
custom_properties = Twig::Branch.all_property_names
|
153
57
|
|
154
58
|
option_parser = OptionParser.new do |opts|
|
155
|
-
opts.banner =
|
59
|
+
opts.banner = Help.intro
|
156
60
|
opts.summary_indent = ' ' * 2
|
157
61
|
opts.summary_width = 32
|
158
62
|
|
63
|
+
###
|
159
64
|
|
160
|
-
|
161
|
-
help_separator(opts, 'Common options:')
|
65
|
+
Help.header(opts, 'Common options')
|
162
66
|
|
163
67
|
desc = 'Use a specific branch.'
|
164
68
|
opts.on(
|
165
|
-
'-b BRANCH', '--branch BRANCH', *
|
69
|
+
'-b BRANCH', '--branch BRANCH', *Help.description(desc)
|
166
70
|
) do |branch|
|
167
71
|
set_option(:branch, branch)
|
168
72
|
end
|
169
73
|
|
170
74
|
desc = 'Unset a branch property.'
|
171
|
-
opts.on('--unset PROPERTY', *
|
75
|
+
opts.on('--unset PROPERTY', *Help.description(desc)) do |property_name|
|
172
76
|
set_option(:unset_property, property_name)
|
173
77
|
end
|
174
78
|
|
175
79
|
desc = 'Show this help content.'
|
176
|
-
opts.on('--help', *
|
80
|
+
opts.on('--help', *Help.description(desc)) do
|
177
81
|
summary_lines = opts.to_s.split("\n")
|
178
82
|
run_pager
|
179
83
|
|
@@ -183,7 +87,7 @@ class Twig
|
|
183
87
|
# Squash successive blank lines
|
184
88
|
next if line == "\n" && prev_line == "\n"
|
185
89
|
|
186
|
-
unless
|
90
|
+
unless Help.line_for_custom_property?(line)
|
187
91
|
puts line
|
188
92
|
prev_line = line
|
189
93
|
end
|
@@ -193,17 +97,18 @@ class Twig
|
|
193
97
|
end
|
194
98
|
|
195
99
|
desc = 'Show Twig version.'
|
196
|
-
opts.on('--version', *
|
197
|
-
puts Twig::VERSION
|
100
|
+
opts.on('--version', *Help.description(desc)) do
|
101
|
+
puts Twig::VERSION
|
102
|
+
exit
|
198
103
|
end
|
199
104
|
|
105
|
+
###
|
200
106
|
|
201
|
-
|
202
|
-
help_separator(opts, 'Filtering branches:')
|
107
|
+
Help.header(opts, 'Filtering branches')
|
203
108
|
|
204
109
|
desc = 'Only list branches below a given age.'
|
205
110
|
opts.on(
|
206
|
-
'--max-days-old AGE', *
|
111
|
+
'--max-days-old AGE', *Help.description(desc, :add_blank_line => true)
|
207
112
|
) do |age|
|
208
113
|
set_option(:max_days_old, age)
|
209
114
|
end
|
@@ -211,13 +116,13 @@ class Twig
|
|
211
116
|
desc = 'Only list branches whose name matches a given pattern.'
|
212
117
|
opts.on(
|
213
118
|
'--only-branch PATTERN',
|
214
|
-
*
|
119
|
+
*Help.description(desc, :add_blank_line => true)
|
215
120
|
) do |pattern|
|
216
121
|
set_option(:property_only, :branch => pattern)
|
217
122
|
end
|
218
123
|
|
219
124
|
desc = 'Do not list branches whose name matches a given pattern.'
|
220
|
-
opts.on('--except-branch PATTERN', *
|
125
|
+
opts.on('--except-branch PATTERN', *Help.description(desc)) do |pattern|
|
221
126
|
set_option(:property_except, :branch => pattern)
|
222
127
|
end
|
223
128
|
|
@@ -232,43 +137,43 @@ class Twig
|
|
232
137
|
set_option(:property_except, property_name_sym => pattern)
|
233
138
|
end
|
234
139
|
end
|
235
|
-
|
140
|
+
Help.description_for_custom_property(opts, [
|
236
141
|
['--only-PROPERTY PATTERN', 'Only list branches with a given property'],
|
237
142
|
['', 'that matches a given pattern.']
|
238
143
|
], :trailing => '')
|
239
|
-
|
144
|
+
Help.description_for_custom_property(opts, [
|
240
145
|
['--except-PROPERTY PATTERN', 'Do not list branches with a given property'],
|
241
146
|
['', 'that matches a given pattern.']
|
242
147
|
])
|
243
148
|
|
244
149
|
desc =
|
245
|
-
'Print branch properties in a format that can be used by other '
|
150
|
+
'Print branch properties in a format that can be used by other ' \
|
246
151
|
'tools. Currently, the only supported value is `json`.'
|
247
152
|
opts.on(
|
248
|
-
'--format FORMAT', *
|
153
|
+
'--format FORMAT', *Help.description(desc, :add_blank_line => true)
|
249
154
|
) do |format|
|
250
155
|
set_option(:format, format)
|
251
156
|
end
|
252
157
|
|
253
158
|
desc =
|
254
|
-
'Lists all branches regardless of other filtering options. '
|
159
|
+
'Lists all branches regardless of other filtering options. ' \
|
255
160
|
'Useful for overriding options in ' +
|
256
161
|
File.basename(Twig::Options::CONFIG_PATH) + '.'
|
257
|
-
opts.on('--all', *
|
162
|
+
opts.on('--all', *Help.description(desc)) do |pattern|
|
258
163
|
unset_option(:max_days_old)
|
259
164
|
unset_option(:property_except)
|
260
165
|
unset_option(:property_only)
|
261
166
|
end
|
262
167
|
|
168
|
+
###
|
263
169
|
|
264
|
-
|
265
|
-
help_separator(opts, 'Listing branches:')
|
170
|
+
Help.header(opts, 'Listing branches')
|
266
171
|
|
267
172
|
desc = <<-DESC
|
268
173
|
Set the width for the `branch` column.
|
269
174
|
(Default: #{Twig::DEFAULT_BRANCH_COLUMN_WIDTH})
|
270
175
|
DESC
|
271
|
-
opts.on('--branch-width NUMBER', *
|
176
|
+
opts.on('--branch-width NUMBER', *Help.description(desc)) do |width|
|
272
177
|
set_option(:property_width, :branch => width)
|
273
178
|
end
|
274
179
|
|
@@ -277,7 +182,7 @@ class Twig
|
|
277
182
|
set_option(:property_width, property_name.to_sym => width)
|
278
183
|
end
|
279
184
|
end
|
280
|
-
|
185
|
+
Help.description_for_custom_property(opts, [
|
281
186
|
['--PROPERTY-width NUMBER', "Set the width for a given property's column."],
|
282
187
|
['', "(Default: #{Twig::DEFAULT_PROPERTY_COLUMN_WIDTH})"]
|
283
188
|
])
|
@@ -288,7 +193,7 @@ class Twig
|
|
288
193
|
DESC
|
289
194
|
opts.on(
|
290
195
|
'--only-property PATTERN',
|
291
|
-
*
|
196
|
+
*Help.description(desc, :add_blank_line => true)
|
292
197
|
) do |pattern|
|
293
198
|
set_option(:property_only_name, pattern)
|
294
199
|
end
|
@@ -299,17 +204,17 @@ class Twig
|
|
299
204
|
DESC
|
300
205
|
opts.on(
|
301
206
|
'--except-property PATTERN',
|
302
|
-
*
|
207
|
+
*Help.description(desc, :add_blank_line => true)
|
303
208
|
) do |pattern|
|
304
209
|
set_option(:property_except_name, pattern)
|
305
210
|
end
|
306
211
|
|
307
|
-
colors = Twig::Display::COLORS.keys.
|
308
|
-
format_string(value, :color => value)
|
309
|
-
|
310
|
-
weights = Twig::Display::WEIGHTS.keys.
|
311
|
-
format_string(value, :weight => value)
|
312
|
-
|
212
|
+
colors = Twig::Display::COLORS.keys.
|
213
|
+
map { |value| format_string(value, :color => value) }.
|
214
|
+
join(', ')
|
215
|
+
weights = Twig::Display::WEIGHTS.keys.
|
216
|
+
map { |value| format_string(value, :weight => value) }.
|
217
|
+
join(' and ')
|
313
218
|
default_header_style = format_string(
|
314
219
|
Twig::DEFAULT_HEADER_COLOR.to_s,
|
315
220
|
:color => Twig::DEFAULT_HEADER_COLOR
|
@@ -321,19 +226,19 @@ class Twig
|
|
321
226
|
DESC
|
322
227
|
opts.on(
|
323
228
|
'--header-style "STYLE"',
|
324
|
-
*
|
229
|
+
*Help.description(desc, :add_blank_line => true)
|
325
230
|
) do |style|
|
326
231
|
set_option(:header_style, style)
|
327
232
|
end
|
328
233
|
|
329
234
|
desc = 'Show oldest branches first. (Default: false)'
|
330
|
-
opts.on('--reverse', *
|
235
|
+
opts.on('--reverse', *Help.description(desc)) do
|
331
236
|
set_option(:reverse, true)
|
332
237
|
end
|
333
238
|
|
239
|
+
###
|
334
240
|
|
335
|
-
|
336
|
-
help_separator(opts, 'GitHub integration:')
|
241
|
+
Help.header(opts, 'GitHub integration')
|
337
242
|
|
338
243
|
desc = <<-DESC
|
339
244
|
Set a custom GitHub API URI prefix, e.g.,
|
@@ -342,7 +247,7 @@ class Twig
|
|
342
247
|
DESC
|
343
248
|
opts.on(
|
344
249
|
'--github-api-uri-prefix PREFIX',
|
345
|
-
*
|
250
|
+
*Help.description(desc, :add_blank_line => true)
|
346
251
|
) do |prefix|
|
347
252
|
set_option(:github_api_uri_prefix, prefix)
|
348
253
|
end
|
@@ -352,31 +257,78 @@ class Twig
|
|
352
257
|
https://github-enterprise.example.com.
|
353
258
|
(Default: "#{Twig::DEFAULT_GITHUB_URI_PREFIX}")
|
354
259
|
DESC
|
355
|
-
opts.on(
|
356
|
-
'--github-uri-prefix PREFIX',
|
357
|
-
*help_description(desc, :add_separator => true)
|
358
|
-
) do |prefix|
|
260
|
+
opts.on('--github-uri-prefix PREFIX', *Help.description(desc)) do |prefix|
|
359
261
|
set_option(:github_uri_prefix, prefix)
|
360
262
|
end
|
361
263
|
|
264
|
+
###
|
265
|
+
|
266
|
+
Help.header(opts, 'Config files and tab completion', :trailing => '')
|
362
267
|
|
268
|
+
Help.print_paragraph(opts, %{
|
269
|
+
Twig can automatically set up a config file for you, where you can put
|
270
|
+
your most frequently used options for filtering and listing branches.
|
271
|
+
To get started, run `twig init` and follow the instructions. This does
|
272
|
+
two things:
|
273
|
+
})
|
363
274
|
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
})
|
275
|
+
Help.print_paragraph(opts, %{
|
276
|
+
* Creates #{Twig::Options::CONFIG_PATH}, where you can put your
|
277
|
+
favorite options, e.g.:
|
278
|
+
})
|
368
279
|
|
369
|
-
|
280
|
+
Help.print_section(opts, [
|
370
281
|
' except-branch: staging',
|
371
282
|
' header-style: green bold',
|
372
283
|
' max-days-old: 30',
|
373
284
|
' reverse: true'
|
285
|
+
].join("\n"))
|
286
|
+
|
287
|
+
Help.print_paragraph(opts, %{
|
288
|
+
* Enables tab completion for Twig subcommands and branch names, e.g.:
|
289
|
+
})
|
290
|
+
|
291
|
+
Help.print_section(opts, [
|
292
|
+
' `twig cre<tab>` -> `twig create-branch`',
|
293
|
+
' `twig status -b my-br<tab>` -> `twig status -b my-branch`'
|
374
294
|
].join("\n"), :trailing => '')
|
375
295
|
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
296
|
+
###
|
297
|
+
|
298
|
+
Help.header(opts, 'Subcommands', :trailing => '')
|
299
|
+
|
300
|
+
Help.print_paragraph(opts, "Twig comes with these subcommands:", :trailing => "\n\n")
|
301
|
+
|
302
|
+
Help.subcommand_descriptions.each do |desc|
|
303
|
+
Help.print_line(opts, desc)
|
304
|
+
end
|
305
|
+
|
306
|
+
Help.subheader(opts, 'Writing a subcommand', :trailing => '')
|
307
|
+
|
308
|
+
Help.print_paragraph(opts, %{
|
309
|
+
You can write any Twig subcommand that fits your own Git workflow. To
|
310
|
+
write a Twig subcommand:
|
311
|
+
})
|
312
|
+
|
313
|
+
Help.print_section(opts, [
|
314
|
+
'1. Write a script; any language will do. (If you want to take',
|
315
|
+
' advantage of Twig\'s option parsing and branch processing, you\'ll',
|
316
|
+
' need Ruby. See `twig-checkout-parent` for an example.)'
|
317
|
+
].join("\n"))
|
318
|
+
|
319
|
+
Help.print_section(opts, [
|
320
|
+
'2. Save it with the `twig-` prefix in your `$PATH`,',
|
321
|
+
' e.g., `~/bin/twig-my-subcommand`.'
|
322
|
+
].join("\n"))
|
323
|
+
|
324
|
+
Help.print_section(opts, [
|
325
|
+
'3. Make it executable: `chmod ugo+x ~/bin/twig-my-subcommand`'
|
326
|
+
].join("\n"))
|
327
|
+
|
328
|
+
Help.print_section(opts, [
|
329
|
+
'4. Run your subcommand: `twig my-subcommand` (with a *space* after',
|
330
|
+
' `twig`'
|
331
|
+
].join("\n"))
|
380
332
|
end
|
381
333
|
|
382
334
|
option_parser.parse!(args)
|
@@ -387,22 +339,12 @@ class Twig
|
|
387
339
|
end
|
388
340
|
|
389
341
|
def abort_for_option_exception(exception)
|
390
|
-
puts exception.message + "\nFor a list of options, run `twig
|
342
|
+
puts exception.message + "\nFor a list of options, run `twig help`."
|
391
343
|
exit 1
|
392
344
|
end
|
393
345
|
|
394
|
-
def exec_subcommand_if_any(args)
|
395
|
-
# Run subcommand binary, if any, and exit here
|
396
|
-
possible_subcommand_name = Twig::Subcommands::BIN_PREFIX + args[0]
|
397
|
-
command_path = Twig.run("which #{possible_subcommand_name} 2>/dev/null")
|
398
|
-
unless command_path.empty?
|
399
|
-
command = ([command_path] + args[1..-1]).join(' ')
|
400
|
-
exec(command)
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
346
|
def read_cli_args!(args)
|
405
|
-
exec_subcommand_if_any(args) if args.any?
|
347
|
+
Twig::Subcommands.exec_subcommand_if_any(args) if args.any?
|
406
348
|
|
407
349
|
args = read_cli_options!(args)
|
408
350
|
branch_name = target_branch_name
|
@@ -436,34 +378,27 @@ class Twig
|
|
436
378
|
end
|
437
379
|
|
438
380
|
def get_branch_property_for_cli(branch_name, property_name)
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
%{The branch "#{branch_name}" does not have the property "#{property_name}".}
|
446
|
-
end
|
447
|
-
rescue ArgumentError, Twig::Branch::MissingPropertyError => exception
|
448
|
-
abort exception.message
|
381
|
+
value = get_branch_property(branch_name, property_name)
|
382
|
+
if value
|
383
|
+
puts value
|
384
|
+
else
|
385
|
+
raise Twig::Branch::MissingPropertyError,
|
386
|
+
%{The branch "#{branch_name}" does not have the property "#{property_name}".}
|
449
387
|
end
|
388
|
+
rescue ArgumentError, Twig::Branch::MissingPropertyError => exception
|
389
|
+
abort exception.message
|
450
390
|
end
|
451
391
|
|
452
392
|
def set_branch_property_for_cli(branch_name, property_name, property_value)
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
abort exception.message
|
457
|
-
end
|
393
|
+
puts set_branch_property(branch_name, property_name, property_value)
|
394
|
+
rescue ArgumentError, RuntimeError => exception
|
395
|
+
abort exception.message
|
458
396
|
end
|
459
397
|
|
460
398
|
def unset_branch_property_for_cli(branch_name, property_name)
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
abort exception.message
|
465
|
-
end
|
399
|
+
puts unset_branch_property(branch_name, property_name)
|
400
|
+
rescue ArgumentError, Twig::Branch::MissingPropertyError => exception
|
401
|
+
abort exception.message
|
466
402
|
end
|
467
|
-
|
468
403
|
end
|
469
404
|
end
|