twig 1.0.0

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/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ _site/
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.8.7-p358@twig --create
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,26 @@
1
+ How to contribute
2
+ =================
3
+
4
+ Let's make life easier for people with lots of Git branches.
5
+
6
+ Found a bug or have a suggestion? [Please open an issue][issues] or ping
7
+ [@ronalddevera on Twitter][twitter].
8
+
9
+ If you want to hack on some code, even better! Here are the basics:
10
+
11
+ 1. Fork the Twig repo.
12
+ 2. Check out the [**`development`** branch][dev branch]; the `master` branch is
13
+ for stable builds only.
14
+ 3. Run the tests to make sure that they pass on your machine: `bundle && rake`
15
+ 4. Add one or more failing tests for your feature or bug fix.
16
+ 5. Write your feature or bug fix to make the test(s) pass.
17
+ 6. Test the change manually:
18
+ 1. `gem build twig.gemspec`
19
+ 2. `gem install twig-x.y.z.gem` (fill in the current version number)
20
+ 7. Push to your fork and submit a pull request.
21
+
22
+ Thanks for contributing!
23
+
24
+ [issues]: https://github.com/rondevera/twig/issues
25
+ [twitter]: https://twitter.com/ronalddevera
26
+ [dev branch]: https://github.com/rondevera/twig/commits/development
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/HISTORY.md ADDED
@@ -0,0 +1,6 @@
1
+ Twig
2
+ ====
3
+
4
+ 1.0
5
+ ---
6
+ * Initial release.
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Ron DeVera
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,232 @@
1
+ Twig: Your personal Git branch assistant.
2
+ =========================================
3
+
4
+ It's hard enough trying to remember the names of all of your Git branches. You
5
+ also need those branches' issue tracker ids, issue statuses, and reminders of
6
+ what to do next with each branch. `git branch` only lists them in alphabetical
7
+ order, which just doesn't cut it.
8
+
9
+ **Twig shows you your most recent branches, and remembers branch details for
10
+ you.** It supports subcommands, like automatically fetching statuses from your
11
+ issue tracking system. It's flexible enough to fit your everyday Git workflow,
12
+ and will save you a ton of time.
13
+
14
+ Here's how Twig looks in action:
15
+
16
+ $ twig
17
+
18
+ issue status todo branch
19
+ ----- ------ ---- ------
20
+ 2013-01-26 18:00:21 (7m ago) 486 In progress Rebase optimize-all-the-things
21
+ 2013-01-26 16:49:21 (2h ago) 268 In progress - whitespace-all-the-things
22
+ 2013-01-23 18:35:21 (3d ago) 159 Shipped Test in prod * refactor-all-the-things
23
+ 2013-01-22 17:12:09 (4d ago) - - - development
24
+ 2013-01-20 19:45:42 (6d ago) - - - master
25
+
26
+
27
+ Installation
28
+ ============
29
+
30
+ gem install twig
31
+
32
+
33
+ Usage
34
+ =====
35
+
36
+ Twig lets you get/set custom properties for each branch, and list branches
37
+ chronologically with their properties.
38
+
39
+ * `twig`: List all branches with properties, newest first
40
+ * `twig <property>`: Get a property for the current branch
41
+ * `twig <property> <value>`: Set a property for the current branch
42
+ * `twig --unset <property>`: Unset a property for the current branch
43
+ * `twig <property> -b <branch>`: Get property for any branch
44
+ * `twig <property> <value> -b <branch>`: Set property for any branch
45
+ * `twig --unset <property> -b <branch>`: Unset property for any branch
46
+ * `twig --help`: More info
47
+
48
+
49
+ Filtering branches
50
+ ------------------
51
+
52
+ Twig lists all of your branches by default (newest first), but you can filter
53
+ them by name and age:
54
+
55
+ * `twig --only-branch <pattern>`:
56
+ Only list branches whose name matches a given pattern
57
+ * `twig --except-branch <pattern>`:
58
+ Don't list branches whose name matches a given pattern
59
+ * `twig --max-days-old <age>`:
60
+ Only list branches below a given age
61
+ * `twig --all`:
62
+ List all branches regardless of other filtering options
63
+
64
+ You can put your most frequently used options into `~/.twigrc`, and they'll be
65
+ automatically included when you run `twig`. Example:
66
+
67
+ # ~/.twigrc:
68
+ except-branch: staging
69
+ max-days-old: 30
70
+
71
+
72
+ Examples
73
+ --------
74
+
75
+ List your branches, and highlight the current branch:
76
+
77
+ $ twig
78
+
79
+ 2013-01-26 18:07:21 (7m ago) * refactor-all-the-things
80
+ 2013-01-24 17:12:09 (2d ago) development
81
+ 2013-01-23 19:45:42 (3d ago) master
82
+
83
+ Remember a branch's issue tracker id:
84
+
85
+ $ git checkout my-branch
86
+ Switched to branch 'my-branch'.
87
+
88
+ $ twig issue 123
89
+ Saved property "issue" as "123" for branch "my-branch".
90
+ # Nearly any property name will do, like "bug" or "ticket".
91
+
92
+ $ twig issue
93
+ 123
94
+
95
+ $ open "https://github.com/myname/myproject/issues/`twig issue`"
96
+ # Opens a browser window for this GitHub issue (in OS X).
97
+
98
+ Keep notes on what you need to do with each branch:
99
+
100
+ $ twig todo "Run tests"
101
+ Saved property "todo" as "Run tests" for branch "my-branch".
102
+
103
+ $ twig todo "Deploy" -b finished-branch
104
+ Saved property "todo" as "Deploy" for branch "finished-branch".
105
+
106
+ $ twig
107
+
108
+ todo branch
109
+ ---- ------
110
+ 2013-01-26 18:00:25 (7m ago) Run tests * my-branch
111
+ 2013-01-23 18:35:12 (3d ago) Deploy finished-branch
112
+ 2013-01-22 17:12:23 (4d ago) - master
113
+
114
+ Remember the order in which you were rebasing your stack of branches:
115
+
116
+ $ git checkout master
117
+ Switched to branch 'master'.
118
+
119
+ $ twig rebase-onto branch2 -b branch3
120
+ Saved property "rebase-onto" as "branch2" for branch "branch3".
121
+
122
+ $ twig rebase-onto branch1 -b branch2
123
+ Saved property "rebase-onto" as "branch1" for branch "branch2".
124
+
125
+ $ twig
126
+
127
+ rebase-onto branch
128
+ ----------- ------
129
+ 2013-01-26 18:00:25 (7m ago) branch2 branch3
130
+ 2013-01-26 16:49:47 (2h ago) branch1 branch2
131
+ 2013-01-23 18:35:12 (3d ago) - branch1
132
+ 2013-01-22 17:12:23 (4d ago) - * master
133
+
134
+ You can set just about any custom property you need to remember for each branch.
135
+
136
+
137
+ Subcommands
138
+ ===========
139
+
140
+ Twig comes with two subcommands, `gh-open` and `gh-update`, which are handy for
141
+ use with GitHub repositories.
142
+
143
+ While inside a Git repo, run `twig gh-open` to see the repo's GitHub URL, and open
144
+ a browser window if possible:
145
+
146
+ $ cd myproject
147
+
148
+ $ twig gh-open
149
+ GitHub URL: https://github.com/myname/myproject
150
+ # Also opens a browser window (OS X only).
151
+
152
+ If you're working on an issue for a GitHub repository, you can also use the
153
+ `gh-update` subcommand:
154
+
155
+ $ git checkout add-feature
156
+ Switched to branch 'add-feature'.
157
+
158
+ $ twig issue 222
159
+ Saved property "issue" as "222" for branch "add-feature".
160
+
161
+ $ twig
162
+
163
+ issue status branch
164
+ ----- ------ ------
165
+ 2013-01-26 18:00:25 (7m ago) 222 - * add-feature
166
+ 2013-01-23 18:35:12 (3d ago) 111 - fix-bug
167
+ 2013-01-22 17:12:23 (4d ago) - - master
168
+
169
+ $ twig gh-update
170
+ # Automatically looks up the GitHub issue status for each
171
+ # of your local branches, and saves it locally.
172
+
173
+ $ twig
174
+
175
+ issue status branch
176
+ ----- ------ ------
177
+ 2013-01-26 18:00:25 (7m ago) 222 open * add-feature
178
+ 2013-01-23 18:35:12 (3d ago) 111 closed fix-bug
179
+ 2013-01-22 17:12:23 (4d ago) - - master
180
+
181
+ Run `twig gh-update` periodically to keep up with GitHub issues locally.
182
+
183
+ You can write any Twig subcommand that fits your own Git workflow. To write a
184
+ Twig subcommand:
185
+
186
+ 1. Write a script. Any language will do. (If you want to take advantage of
187
+ Twig's option parsing and branch processing, you'll need Ruby. See
188
+ [`bin/twig-gh-update`][twig-gh-update] for an example.)
189
+ 2. Save it with the `twig-` prefix in your `$PATH`,
190
+ e.g., `~/bin/twig-my-subcommand`.
191
+ 3. Make it executable: `chmod ugo+x ~/bin/twig-my-subcommand`
192
+ 4. Run your subcommand: `twig my-subcommand` (with a *space* after `twig`)
193
+
194
+ [twig-gh-update]: https://github.com/rondevera/twig/blob/master/bin/twig-gh-update
195
+
196
+ Some ideas for subcommands:
197
+
198
+ * Get each branch's status for any issue tracking system that has an API,
199
+ like [JIRA](http://www.atlassian.com/software/jira/overview),
200
+ [FogBugz](http://www.fogcreek.com/fogbugz/), or
201
+ [Lighthouse](http://lighthouseapp.com/).
202
+ * Given an issue tracker id, check out that issue's branch locally. Great for
203
+ following teammates' branches, remembering their issue ids, and knowing when
204
+ they've shipped.
205
+ * Generate a formatted list of your branches from the past week. Useful for
206
+ emailing your team about what you're up to.
207
+
208
+ If you write a subcommand that others can appreciate, send a pull request or add
209
+ it to the [Twig wiki][wiki]!
210
+
211
+
212
+ More info
213
+ =========
214
+
215
+ - **Requirements:** Tested with Git 1.6.5 and Ruby 1.8.7. Probably works with
216
+ older software, but it's not guaranteed.
217
+ - **Contributing:** Found a bug or have a suggestion? [Please open an
218
+ issue][issues] or ping [@ronalddevera on Twitter][twitter]. If you want to
219
+ hack on some features or contribute a subcommand you've written, feel free to
220
+ fork and send a pull request for the **[development branch][dev branch]**.
221
+ (The master branch is for stable builds only.) See the full details in the
222
+ [Contributing][contributing] instructions.
223
+ - **History:** [History/changelog for Twig][history]
224
+ - **License:** Twig is released under the [MIT License][license].
225
+
226
+ [issues]: https://github.com/rondevera/twig/issues
227
+ [wiki]: https://github.com/rondevera/twig/wiki
228
+ [twitter]: https://twitter.com/ronalddevera
229
+ [dev branch]: https://github.com/rondevera/twig/commits/development
230
+ [contributing]: https://github.com/rondevera/twig/blob/master/CONTRIBUTING.md
231
+ [history]: https://github.com/rondevera/twig/blob/master/HISTORY.md
232
+ [license]: https://github.com/rondevera/twig/blob/master/LICENSE.md
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'rspec/core/rake_task'
3
+ require 'bundler'
4
+
5
+ desc 'Run specs'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+
10
+ Bundler::GemHelper.install_tasks
data/bin/twig ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twig')
4
+
5
+ twig = Twig.new
6
+ abort unless twig.repo?
7
+
8
+ # Gettin' twiggy wit' it.
9
+ twig.read_config_file!
10
+ twig.read_cli_args!(ARGV)
data/bin/twig-gh-open ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Opens a browser window for the current GitHub repo.
4
+ #
5
+ # Author: Ron DeVera <http://rondevera.com>
6
+
7
+ class TwigGithubRepo
8
+ def initialize
9
+ if origin_url.empty? || username.empty? || repository.empty?
10
+ abort_for_non_github_repo
11
+ end
12
+
13
+ yield(self)
14
+ end
15
+
16
+ def origin_url
17
+ @origin_url ||= `git config remote.origin.url`.strip
18
+ end
19
+
20
+ def origin_url_parts
21
+ @origin_url_parts ||= origin_url.split(/[\/:]/)
22
+ end
23
+
24
+ def username
25
+ @username ||= origin_url_parts[-2] || ''
26
+ end
27
+
28
+ def repository
29
+ @repo ||= origin_url_parts[-1].sub(/\.git$/, '') || ''
30
+ end
31
+
32
+ def abort_for_non_github_repo
33
+ abort 'This does not appear to be a GitHub repository.'
34
+ end
35
+ end
36
+
37
+ TwigGithubRepo.new do |gh_repo|
38
+ url = "https://github.com/#{gh_repo.username}/#{gh_repo.repository}"
39
+ puts "GitHub URL: #{url}"
40
+ `which open && open #{url}`
41
+ end
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Updates each branch with the latest issue status on GitHub.
4
+ #
5
+ # Author: Ron DeVera <http://rondevera.com>
6
+
7
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twig')
8
+ require 'rubygems'
9
+ require 'json'
10
+ require 'net/https'
11
+ require 'uri'
12
+
13
+ class TwigGithubRepo
14
+ def initialize
15
+ if origin_url.empty? || username.empty? || repository.empty?
16
+ abort_for_non_github_repo
17
+ end
18
+
19
+ yield(self)
20
+ end
21
+
22
+ def origin_url
23
+ @origin_url ||= `git config remote.origin.url`.strip
24
+ end
25
+
26
+ def origin_url_parts
27
+ @origin_url_parts ||= origin_url.split(/[\/:]/)
28
+ end
29
+
30
+ def username
31
+ @username ||= origin_url_parts[-2] || ''
32
+ end
33
+
34
+ def repository
35
+ @repo ||= origin_url_parts[-1].sub(/\.git$/, '') || ''
36
+ end
37
+
38
+ def abort_for_non_github_repo
39
+ abort 'This does not appear to be a GitHub repository.'
40
+ end
41
+ end
42
+
43
+ twig = Twig.new
44
+ twig.read_config_file!
45
+ twig.read_cli_options!(ARGV)
46
+
47
+ TwigGithubRepo.new do |gh_repo|
48
+ $stdout.sync = true
49
+ print 'Getting latest states for GitHub issues...'
50
+
51
+ issues = {}
52
+ issues_uri_base =
53
+ "https://api.github.com/repos/#{gh_repo.username}/#{gh_repo.repository}/issues"
54
+ issues_uris = [
55
+ URI.parse("#{issues_uri_base}?state=open"),
56
+ URI.parse("#{issues_uri_base}?state=closed")
57
+ ]
58
+
59
+ begin
60
+ issues_uris.each do |issues_uri|
61
+ http = Net::HTTP.new(issues_uri.host, issues_uri.port)
62
+ http.use_ssl = true
63
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
64
+ request = Net::HTTP::Get.new(issues_uri.path + '?' + issues_uri.query)
65
+ response = http.request(request)
66
+
67
+ if response.code.to_i == 200
68
+ issues_data = JSON.parse(response.body)
69
+ issues_data.each do |issue_data|
70
+ issues[issue_data['number']] = issue_data
71
+ end
72
+ else
73
+ puts "\nERROR: Couldn't get open issues from GitHub. " <<
74
+ "(Response: #{response.code})"
75
+ end
76
+ end
77
+ end
78
+
79
+ twig.branches.each do |branch|
80
+ issue_number = branch.get_property('issue').to_i
81
+ next unless issue_number > 0
82
+
83
+ issue = issues[issue_number]
84
+ next unless issue
85
+
86
+ state = issue['state']
87
+ branch.set_property('status', state) unless state.nil? || state.empty?
88
+
89
+ print '.'
90
+ end
91
+
92
+ puts
93
+ end
data/bin/twig-help ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Your friendly neighborhood `twig --help` alias.
4
+ puts `twig --help`
data/install ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ twig_bin_path = File.expand_path(File.dirname(__FILE__)) + '/bin/**/twig*'
4
+ twig_bins = Dir.glob(twig_bin_path)
5
+ user_bin_path = '~/bin'
6
+
7
+ twig_bins.each do |bin|
8
+ `ln -fs #{bin} #{user_bin_path}`
9
+ end
10
+
11
+ if `which twig` && $?.success?
12
+ puts 'All set! Run `twig` to list your local branches.'
13
+ puts 'For more info, run `twig --help`.'
14
+ else
15
+ puts 'Argh! Something went wrong. Please try symlinking the files in '
16
+ puts '`./bin/` to `~/bin/` and ensure that `~/bin/` is in your `$PATH`, or '
17
+ puts 'contact rondevera on GitHub.'
18
+ end
@@ -0,0 +1,72 @@
1
+ class Twig
2
+ class Branch
3
+
4
+ RESERVED_BRANCH_PROPERTIES = %w[merge rebase remote]
5
+ PROPERTY_NAME_FROM_GIT_CONFIG = /^branch\.[^.]+\.([^=]+)=.*$/
6
+
7
+ attr_accessor :name, :last_commit_time
8
+
9
+ def self.all_properties
10
+ @_all_properties ||= begin
11
+ config_lines = Twig.run('git config --list').split("\n")
12
+
13
+ properties = config_lines.map do |line|
14
+ # Split by rightmost `=`, allowing branch names to contain `=`:
15
+ key, value = line.match(/(.+)=(.+)/)[1..2]
16
+
17
+ key_parts = key.split('.')
18
+ key_parts.last if key_parts[0] == 'branch' && key_parts.size > 2
19
+ end.compact
20
+
21
+ properties.uniq.sort - RESERVED_BRANCH_PROPERTIES
22
+ end
23
+ end
24
+
25
+ def initialize(name, attrs = {})
26
+ self.name = name
27
+ raise ArgumentError, '`name` is required' if name.empty?
28
+
29
+ self.last_commit_time = attrs[:last_commit_time]
30
+ end
31
+
32
+ def to_s ; name ; end
33
+
34
+ def sanitize_property(property_name)
35
+ property_name.gsub(/[ _]+/, '')
36
+ end
37
+
38
+ def get_property(property_name)
39
+ Twig.run("git config branch.#{name}.#{property_name}")
40
+ end
41
+
42
+ def set_property(property_name, value)
43
+ property_name = sanitize_property(property_name)
44
+ value = value.to_s.strip
45
+
46
+ if RESERVED_BRANCH_PROPERTIES.include?(property_name)
47
+ %{Can't modify the reserved property "#{property_name}".}
48
+ elsif value.empty?
49
+ %{Can't set a branch property to an empty string.}
50
+ else
51
+ Twig.run(%{git config branch.#{name}.#{property_name} "#{value}"})
52
+ result_body = %{property "#{property_name}" as "#{value}" for branch "#{name}".}
53
+ if $?.success?
54
+ "Saved #{result_body}"
55
+ else
56
+ "Could not save #{result_body}"
57
+ end
58
+ end
59
+ end
60
+
61
+ def unset_property(property_name)
62
+ value = get_property(property_name)
63
+ if value && !value.empty?
64
+ Twig.run(%{git config --unset branch.#{name}.#{property_name}})
65
+ %{Removed property "#{property_name}" for branch "#{name}".}
66
+ else
67
+ %{The branch "#{name}" does not have the property "#{property_name}".}
68
+ end
69
+ end
70
+
71
+ end
72
+ end