twig 1.2.1 → 1.3

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md CHANGED
@@ -1,9 +1,22 @@
1
1
  Twig
2
2
  ====
3
3
 
4
+ 1.3 (2013-05-22)
5
+ ----------------
6
+ * ENHANCEMENT: Add `--branch-width` and `--<property>-width` options for setting
7
+ custom column widths.
8
+ * ENHANCEMENT: Add `--reverse` option for listing least recently updated
9
+ branches first. This can be used in a config file as `reverse: true`.
10
+ * ENHANCEMENT: Make `gh-open` and `gh-open-issue` work cross-platform.
11
+ (GH-18. Thanks [ixti](https://github.com/ixti)!)
12
+ * FIX: Allow getting, setting, and unsetting properties for branches older than
13
+ the `max-days-old` option, if given.
14
+ * FIX: Abort `twig gh-*` subcommands early if working in a non-Github
15
+ repository.
16
+
4
17
  1.2.1 (2013-05-04)
5
18
  ------------------
6
- * FIX: Add User-Agent string to `twig-gh-update` GitHub requests to comply with
19
+ * FIX: Add User-Agent string to `twig gh-update` GitHub requests to comply with
7
20
  GitHub API v3.
8
21
 
9
22
  1.2 (2013-03-21)
data/README.md CHANGED
@@ -43,13 +43,21 @@ chronologically with their properties.
43
43
  * `twig <property> -b <branch>`: Get property for any branch
44
44
  * `twig <property> <value> -b <branch>`: Set property for any branch
45
45
  * `twig --unset <property> -b <branch>`: Unset property for any branch
46
- * `twig --header-style <format>`: Change the header style, e.g., "red", "green bold"
47
46
  * `twig init-completion`: Set up tab completion for `-b` and `--branch`
48
47
  * `twig --help`: More info
49
48
 
50
49
 
51
- Filtering branches
52
- ------------------
50
+ Display options
51
+ ---------------
52
+
53
+ * `twig --header-style <format>`: Change the header style, e.g., "red", "green bold"
54
+ * `twig --branch-width <number>`: Set the character width for the `branch` column
55
+ * `twig --<property>-width <number>`: Set the character width for a specific property column
56
+ * `twig --reverse`: List oldest branches first
57
+
58
+
59
+ Filtering options
60
+ -----------------
53
61
 
54
62
  Twig lists all of your branches by default (newest first), but you can filter
55
63
  them by age, name, and custom properties:
@@ -154,7 +162,6 @@ a browser window if possible:
154
162
 
155
163
  $ twig gh-open
156
164
  GitHub URL: https://github.com/myname/myproject
157
- # Also opens a browser window (OS X only).
158
165
 
159
166
  If you're working on an issue for a GitHub repository, the `gh-update`
160
167
  subcommand syncs issue statuses with GitHub:
@@ -193,7 +200,6 @@ subcommand to view that issue on GitHub:
193
200
  # Current branch:
194
201
  $ twig gh-open-issue
195
202
  GitHub issue URL: https://github.com/myname/myproject/issues/111
196
- # Also opens a browser window (OS X only).
197
203
 
198
204
  # Any branch:
199
205
  $ twig gh-open-issue -b <branch name>
data/bin/twig CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'twig'))
4
4
 
5
- twig = Twig.new
6
5
  repo_required = ARGV != ['--help'] && ARGV != ['help'] && ARGV != ['--version']
7
6
 
8
- if repo_required && !twig.repo?
7
+ if repo_required && !Twig.repo?
9
8
  abort "Current directory is not a git repository."
10
9
  end
11
10
 
12
11
  # Gettin' twiggy wit' it.
12
+ twig = Twig.new
13
13
  twig.read_config_file!
14
14
  twig.read_cli_args!(ARGV)
data/bin/twig-gh-open CHANGED
@@ -5,10 +5,11 @@
5
5
  # Author: Ron DeVera <http://rondevera.com>
6
6
 
7
7
  require 'rubygems'
8
- require 'twig/github'
8
+ require 'twig'
9
+ require 'launchy'
9
10
 
10
11
  Twig::GithubRepo.new do |gh_repo|
11
12
  url = "https://github.com/#{gh_repo.username}/#{gh_repo.repository}"
12
13
  puts "GitHub URL: #{url}"
13
- `which open && open #{url}`
14
+ Launchy.open(url) rescue nil
14
15
  end
@@ -10,6 +10,7 @@
10
10
 
11
11
  require 'rubygems'
12
12
  require 'twig'
13
+ require 'launchy'
13
14
 
14
15
  Twig::GithubRepo.new do |gh_repo|
15
16
  twig = Twig.new
@@ -25,5 +26,5 @@ Twig::GithubRepo.new do |gh_repo|
25
26
  url << "/issues/#{issue_id}"
26
27
 
27
28
  puts "GitHub issue URL: #{url}"
28
- `which open && open #{url}`
29
+ Launchy.open(url) rescue nil
29
30
  end
data/lib/twig.rb CHANGED
@@ -18,6 +18,11 @@ class Twig
18
18
  `#{command}`.strip
19
19
  end
20
20
 
21
+ def self.repo?
22
+ Twig.run('git rev-parse 2>&1')
23
+ $?.success?
24
+ end
25
+
21
26
  def initialize
22
27
  self.options = {}
23
28
 
@@ -25,14 +30,8 @@ class Twig
25
30
  set_option(:header_style, DEFAULT_HEADER_COLOR.to_s)
26
31
  end
27
32
 
28
- def repo?
29
- Twig.run('git rev-parse 2>&1')
30
- $?.success?
31
- end
32
-
33
33
  def current_branch_name
34
- @_current_branch_name ||=
35
- Twig.run('git symbolic-ref -q HEAD').sub(%r{^#{ REF_PREFIX }}, '')
34
+ @_current_branch_name ||= Twig.run('git rev-parse --abbrev-ref HEAD')
36
35
  end
37
36
 
38
37
  def all_branches
@@ -84,8 +83,8 @@ class Twig
84
83
  end
85
84
  end
86
85
 
87
- def branch_names
88
- branches.map { |branch| branch.name }
86
+ def all_branch_names
87
+ all_branches.map { |branch| branch.name }
89
88
  end
90
89
 
91
90
 
@@ -103,9 +102,11 @@ class Twig
103
102
 
104
103
  out = "\n" << branch_list_headers(options)
105
104
 
106
- # List most recently modified branches first
107
- listable_branches =
108
- branches.sort_by { |branch| branch.last_commit_time }.reverse
105
+ # List least recently modified branches first
106
+ listable_branches = branches.sort_by { |branch| branch.last_commit_time }
107
+ if options[:reverse] != true
108
+ listable_branches.reverse! # List most recently modified branches first
109
+ end
109
110
 
110
111
  branch_lines = listable_branches.inject([]) do |result, branch|
111
112
  result << branch_list_line(branch)
data/lib/twig/cli.rb CHANGED
@@ -12,7 +12,7 @@ class Twig
12
12
  for your Git branches.
13
13
  })
14
14
 
15
- <<-BANNER.gsub(/^[ ]+/, '')
15
+ intro = <<-BANNER.gsub(/^[ ]+/, '')
16
16
 
17
17
  #{version_string}
18
18
  #{'-' * version_string.size}
@@ -20,8 +20,9 @@ class Twig
20
20
  #{intro}
21
21
 
22
22
  #{Twig::HOMEPAGE}
23
-
24
23
  BANNER
24
+
25
+ intro + ' ' # Force extra blank line
25
26
  end
26
27
 
27
28
  def help_separator(option_parser, text, options={})
@@ -54,11 +55,47 @@ class Twig
54
55
  lines
55
56
  end
56
57
 
58
+ def help_description_for_custom_property(option_parser, desc_lines)
59
+ indent = ' '
60
+ left_column_width = 29
61
+
62
+ help_desc = desc_lines.inject('') do |desc, (left_column, right_column)|
63
+ desc + indent +
64
+ sprintf("%-#{left_column_width}s", left_column) + right_column + "\n"
65
+ end
66
+
67
+ help_separator(option_parser, help_desc, :trailing => "\n")
68
+ end
69
+
57
70
  def help_paragraph(text)
58
71
  help_description(text, :width => 80).join("\n")
59
72
  end
60
73
 
74
+ def help_line_for_custom_property?(line)
75
+ is_custom_property_except = (
76
+ line.include?('--except-') &&
77
+ !line.include?('--except-branch') &&
78
+ !line.include?('--except-PROPERTY')
79
+ )
80
+ is_custom_property_only = (
81
+ line.include?('--only-') &&
82
+ !line.include?('--only-branch') &&
83
+ !line.include?('--only-PROPERTY')
84
+ )
85
+ is_custom_property_width = (
86
+ line =~ /--.+-width/ &&
87
+ !line.include?('--branch-width') &&
88
+ !line.include?('--PROPERTY-width')
89
+ )
90
+
91
+ is_custom_property_except ||
92
+ is_custom_property_only ||
93
+ is_custom_property_width
94
+ end
95
+
61
96
  def read_cli_options!(args)
97
+ custom_properties = Twig::Branch.all_properties
98
+
62
99
  option_parser = OptionParser.new do |opts|
63
100
  opts.banner = help_intro
64
101
  opts.summary_indent = ' ' * 2
@@ -82,22 +119,17 @@ class Twig
82
119
 
83
120
  desc = 'Show this help content.'
84
121
  opts.on('--help', *help_description(desc)) do
85
- summary_lines = opts.to_a
122
+ summary_lines = opts.to_s.split("\n")
86
123
 
87
- # Filter out `--only-PROPERTY` lines
124
+ # Filter out custom property lines
125
+ prev_line = nil
88
126
  summary_lines.each do |line|
89
- is_custom_property_only = (
90
- line.include?('--only-') &&
91
- !line.include?('--only-branch') &&
92
- !line.include?('--only-PROPERTY')
93
- )
94
- is_custom_property_except = (
95
- line.include?('--except-') &&
96
- !line.include?('--except-branch') &&
97
- !line.include?('--except-PROPERTY')
98
- )
99
- unless is_custom_property_only || is_custom_property_except
127
+ # Squash successive blank lines
128
+ next if line == "\n" && prev_line == "\n"
129
+
130
+ unless help_line_for_custom_property?(line)
100
131
  puts line
132
+ prev_line = line
101
133
  end
102
134
  end
103
135
 
@@ -129,16 +161,10 @@ class Twig
129
161
  end
130
162
 
131
163
  desc = 'Do not list branches whose name matches a given pattern.'
132
- opts.on(
133
- '--except-branch PATTERN',
134
- *help_description(desc, :add_separator => false)
135
- # `:add_separator => false` skips the extra line generated by the
136
- # custom property filters below.
137
- ) do |pattern|
164
+ opts.on('--except-branch PATTERN', *help_description(desc)) do |pattern|
138
165
  set_option(:property_except, :branch => pattern)
139
166
  end
140
167
 
141
- custom_properties = Twig::Branch.all_properties
142
168
  custom_properties.each do |property_name|
143
169
  opts.on("--only-#{property_name} PATTERN") do |pattern|
144
170
  set_option(:property_only, property_name.to_sym => pattern)
@@ -148,18 +174,14 @@ class Twig
148
174
  set_option(:property_except, property_name.to_sym => pattern)
149
175
  end
150
176
  end
151
-
152
- custom_properties_desc_lines = [
177
+ help_description_for_custom_property(opts, [
153
178
  ['--only-PROPERTY PATTERN', 'Only list branches with a given property'],
154
179
  ['', 'that matches a given pattern.'],
155
- ['', ''],
180
+ ])
181
+ help_description_for_custom_property(opts, [
156
182
  ['--except-PROPERTY PATTERN', 'Do not list branches with a given property'],
157
183
  ['', 'that matches a given pattern.']
158
- ]
159
- custom_properties_desc = custom_properties_desc_lines.inject('') do |desc, line_parts|
160
- desc + sprintf(' %-29s', line_parts[0]) + line_parts[1] + "\n"
161
- end
162
- help_separator(opts, custom_properties_desc, :trailing => "\n")
184
+ ])
163
185
 
164
186
  desc =
165
187
  'Lists all branches regardless of other filtering options. ' +
@@ -175,11 +197,25 @@ class Twig
175
197
 
176
198
  help_separator(opts, 'Listing branches:')
177
199
 
200
+ desc = 'Set the width for the `branch` column.'
201
+ opts.on('--branch-width NUMBER', *help_description(desc)) do |width|
202
+ set_option(:property_width, :branch => width)
203
+ end
204
+
205
+ custom_properties.each do |property_name|
206
+ opts.on("--#{property_name}-width NUMBER") do |width|
207
+ set_option(:property_width, property_name.to_sym => width)
208
+ end
209
+ end
210
+ help_description_for_custom_property(opts, [
211
+ ['--PROPERTY-width NUMBER', "Set the width for a given property's column."]
212
+ ])
213
+
178
214
  colors = Twig::Display::COLORS.keys.map do |value|
179
- format_string(value, { :color => value })
215
+ format_string(value, :color => value)
180
216
  end.join(', ')
181
217
  weights = Twig::Display::WEIGHTS.keys.map do |value|
182
- format_string(value, { :weight => value })
218
+ format_string(value, :weight => value)
183
219
  end.join(' and ')
184
220
  default_color = format_string(
185
221
  Twig::DEFAULT_HEADER_COLOR.to_s,
@@ -190,10 +226,18 @@ class Twig
190
226
  Valid colors are #{colors}. Valid weights are #{weights}.
191
227
  The default is "#{default_color}".
192
228
  DESC
193
- opts.on('--header-style "STYLE"', *help_description(desc)) do |style|
229
+ opts.on(
230
+ '--header-style "STYLE"',
231
+ *help_description(desc, :add_separator => true)
232
+ ) do |style|
194
233
  set_option(:header_style, style)
195
234
  end
196
235
 
236
+ desc = 'Show oldest branches first.'
237
+ opts.on('--reverse', *help_description(desc)) do
238
+ set_option(:reverse, true)
239
+ end
240
+
197
241
 
198
242
 
199
243
  help_separator(opts, help_paragraph(%{
data/lib/twig/display.rb CHANGED
@@ -14,41 +14,58 @@ class Twig
14
14
  :normal => 0,
15
15
  :bold => 1
16
16
  }
17
+ DEFAULT_PROPERTY_COLUMN_WIDTH = 16
18
+ DEFAULT_BRANCH_COLUMN_WIDTH = 48
17
19
  CURRENT_BRANCH_INDICATOR = '* '
18
20
  EMPTY_BRANCH_PROPERTY_INDICATOR = '-'
19
21
 
20
- def column(string = ' ', num_columns = 1, column_options = {})
22
+ def column(string, options = {})
21
23
  # Returns `string` with an exact fixed width. If `string` is too wide, it
22
24
  # is truncated with an ellipsis and a trailing space to separate columns.
23
25
  #
24
- # `column_options`:
26
+ # `options`:
25
27
  # - `:color`: `nil` by default. Accepts a key from `COLORS`.
26
28
  # - `:weight`: `nil` by default. Accepts a key from `WEIGHTS`.
27
29
  # - `:width`: 8 (characters) by default.
28
30
 
29
- width_per_column = column_options[:width] || 8
30
- total_width = num_columns * width_per_column
31
- new_string = string[0, total_width]
32
- omission = '... '
31
+ string ||= ' '
32
+ width = options[:width] || 8
33
+ new_string = string[0, width]
34
+ omission = '...'
33
35
 
34
- if string.size >= total_width
36
+ if string.size > width
35
37
  new_string[-omission.size, omission.size] = omission
36
38
  else
37
- new_string = ' ' * total_width
39
+ new_string = ' ' * width
38
40
  new_string[0, string.size] = string
39
41
  end
40
42
 
41
43
  new_string = format_string(
42
44
  new_string,
43
- column_options.reject { |k, v| ![:color, :weight].include?(k) }
45
+ options.reject { |k, v| ![:color, :weight].include?(k) }
44
46
  )
45
47
 
46
48
  new_string
47
49
  end
48
50
 
51
+ def date_time_column_width; 35; end
52
+ def column_gutter; ' '; end
53
+
54
+ def property_column_width(property_name = nil)
55
+ if property_name && options[:property_width]
56
+ width = options[:property_width][property_name.to_sym]
57
+ end
58
+
59
+ if width
60
+ width
61
+ elsif property_name == :branch
62
+ Twig::Display::DEFAULT_BRANCH_COLUMN_WIDTH
63
+ else
64
+ Twig::Display::DEFAULT_PROPERTY_COLUMN_WIDTH
65
+ end
66
+ end
67
+
49
68
  def branch_list_headers(header_options = {})
50
- columns_for_date_time = 5
51
- columns_per_property = 2
52
69
  branch_indicator_padding = ' ' * CURRENT_BRANCH_INDICATOR.size
53
70
 
54
71
  header_options.merge!(
@@ -62,22 +79,22 @@ class Twig
62
79
  end
63
80
  )
64
81
 
65
- out =
66
- column(' ', columns_for_date_time) <<
67
- Twig::Branch.all_properties.map do |property|
68
- column(property, columns_per_property, header_options)
69
- end.join <<
70
- column(branch_indicator_padding + 'branch',
71
- columns_per_property, header_options) <<
72
- "\n"
73
- out <<
74
- column(' ', columns_for_date_time) <<
75
- Twig::Branch.all_properties.map do |property|
76
- column('-' * property.size, columns_per_property, header_options)
77
- end.join <<
78
- column(branch_indicator_padding + '------',
79
- columns_per_property, header_options) <<
80
- "\n"
82
+ out = column(' ', :width => date_time_column_width) << column_gutter
83
+ out << Twig::Branch.all_properties.map do |property|
84
+ width = property_column_width(property)
85
+ column(property, header_options.merge(:width => width)) << column_gutter
86
+ end.join
87
+ out << column(branch_indicator_padding + 'branch', header_options)
88
+ out << "\n"
89
+
90
+ out << column(' ', :width => date_time_column_width) << column_gutter
91
+ out << Twig::Branch.all_properties.map do |property|
92
+ width = property_column_width(property)
93
+ underline = '-' * property.size
94
+ column(underline, header_options.merge(:width => width)) << column_gutter
95
+ end.join
96
+ out << column(branch_indicator_padding + '------', header_options)
97
+ out << "\n"
81
98
 
82
99
  out
83
100
  end
@@ -87,24 +104,29 @@ class Twig
87
104
 
88
105
  properties = Twig::Branch.all_properties.inject({}) do |result, property_name|
89
106
  property = (get_branch_property(branch.name, property_name) || '').strip
90
- property = column(EMPTY_BRANCH_PROPERTY_INDICATOR) if property.empty?
107
+ property = EMPTY_BRANCH_PROPERTY_INDICATOR if property.empty?
91
108
  property.gsub!(/[\n\r]+/, ' ')
92
109
  result.merge(property_name => property)
93
110
  end
94
111
 
95
- line = column(branch.last_commit_time.to_s, 5)
112
+ line = column(branch.last_commit_time.to_s, :width => date_time_column_width)
113
+ line << column_gutter
96
114
 
97
115
  line <<
98
116
  Twig::Branch.all_properties.map do |property_name|
99
- property = properties[property_name] || ''
100
- column(property, 2)
117
+ property_value = properties[property_name] || ''
118
+ width = property_column_width(property_name)
119
+ column(property_value, :width => width) << column_gutter
101
120
  end.join
102
121
 
122
+ branch_column_width = property_column_width(:branch)
123
+ branch_column = column(branch.to_s, :width => branch_column_width)
124
+ branch_column.strip! # Strip final column
103
125
  line <<
104
126
  if is_current_branch
105
- CURRENT_BRANCH_INDICATOR + branch.to_s
127
+ CURRENT_BRANCH_INDICATOR + branch_column
106
128
  else
107
- (' ' * CURRENT_BRANCH_INDICATOR.size) + branch.to_s
129
+ (' ' * CURRENT_BRANCH_INDICATOR.size) + branch_column
108
130
  end
109
131
 
110
132
  line = format_string(line, :weight => :bold) if is_current_branch