pgit 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +22 -0
- data/README.markdown +32 -0
- data/Rakefile +24 -0
- data/bin/pgit +97 -0
- data/lib/pgit.rb +14 -0
- data/lib/pgit/configuration.rb +77 -0
- data/lib/pgit/current_branch.rb +23 -0
- data/lib/pgit/current_project.rb +62 -0
- data/lib/pgit/installer.rb +32 -0
- data/lib/pgit/name_parser.rb +57 -0
- data/lib/pgit/story.rb +48 -0
- data/lib/pgit/story_branch.rb +30 -0
- data/lib/pgit/story_branch/application.rb +19 -0
- data/lib/pgit/version.rb +3 -0
- data/lib/pgit_configuration.rb +57 -0
- data/man/pgit.1 +18 -0
- data/man/pgit.1.html +96 -0
- data/man/pgit.1.ronn +15 -0
- data/pgit.gemspec +21 -0
- data/pgit.rdoc +5 -0
- data/spec/pgit/application_spec.rb +16 -0
- data/spec/pgit/configuration_spec.rb +158 -0
- data/spec/pgit/current_branch_spec.rb +72 -0
- data/spec/pgit/current_project_spec.rb +80 -0
- data/spec/pgit/installer_spec.rb +158 -0
- data/spec/pgit/pgit_spec.rb +33 -0
- data/spec/pgit/story_branch/application_spec.rb +48 -0
- data/spec/pgit/story_branch/name_parser_spec.rb +42 -0
- data/spec/pgit/story_branch_spec.rb +90 -0
- data/spec/pgit/story_spec.rb +54 -0
- metadata +110 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a72ba79fbaa4bfba38e13548cff4ac44812fb0b1
|
4
|
+
data.tar.gz: fd94693b8ff782640064f0a134d822b4922ab4bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5491115049d8c35b86df684a793be62ad71a7b3678379a3eb5337be7692c7e67903cd3a088cd0b729c419473db5a0a6d69031ec5386426de3a4e9f0a3cb3de75
|
7
|
+
data.tar.gz: ea5e67ecc7a8ca90e475ec261fec625581b6bbe92bd337d25631f345df57d8efee6dead19f0a49ca9db735da4569d4e152797513b5b1a0c29860feb0d6372d7a
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.un~
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color -fd
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pgit (0.0.2)
|
5
|
+
gli (= 2.12.2)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
coderay (1.1.0)
|
11
|
+
gli (2.12.2)
|
12
|
+
method_source (0.8.2)
|
13
|
+
pry (0.10.1)
|
14
|
+
coderay (~> 1.1.0)
|
15
|
+
method_source (~> 0.8.1)
|
16
|
+
slop (~> 3.4)
|
17
|
+
rake (10.4.2)
|
18
|
+
slop (3.6.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
pgit!
|
25
|
+
pry
|
26
|
+
rake
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Edderic
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.markdown
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# PGit
|
2
|
+
|
3
|
+
## Example Usage
|
4
|
+
|
5
|
+
Assuming that:
|
6
|
+
- you have a project that uses Pivotal Tracker and Git that is listed under ~/.pgit.rc.yml
|
7
|
+
- your working directory is in that project,
|
8
|
+
- you want to automatically branch out based on the story title
|
9
|
+
- the story title with story id 10102004 is "Implement a really cool feature"
|
10
|
+
|
11
|
+
```
|
12
|
+
$ pgit story_branch -s 10102004
|
13
|
+
```
|
14
|
+
|
15
|
+
will create a branch for you named `implement-really-cool-feature-10102004`
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
1. Install via RubyGems:
|
20
|
+
|
21
|
+
```
|
22
|
+
$ gem install pgit
|
23
|
+
```
|
24
|
+
|
25
|
+
2. Setup configuration file:
|
26
|
+
|
27
|
+
```
|
28
|
+
$ pgit install
|
29
|
+
```
|
30
|
+
|
31
|
+
This will generate a YAML file under `~/.pgit.rc.yml`. Edit that file to add information about projects that you're working on. Each project needs a project `id`, Pivotal Tracker `api_token`, and `path`
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rdoc/task'
|
5
|
+
|
6
|
+
Rake::RDocTask.new do |rd|
|
7
|
+
rd.main = "README.rdoc"
|
8
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
9
|
+
rd.title = 'PGit'
|
10
|
+
end
|
11
|
+
|
12
|
+
spec = eval(File.read('pgit.gemspec'))
|
13
|
+
|
14
|
+
Gem::PackageTask.new(spec) do |pkg|
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Run specs'
|
18
|
+
require 'rake/testtask'
|
19
|
+
Rake::TestTask.new(:spec) do |s|
|
20
|
+
s.libs << "spec"
|
21
|
+
s.test_files = FileList['spec/**/*_test.rb']
|
22
|
+
end
|
23
|
+
|
24
|
+
task :default => [:spec]
|
data/bin/pgit
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'gli'
|
3
|
+
require 'pgit'
|
4
|
+
include GLI::App
|
5
|
+
|
6
|
+
program_desc 'Optimize your Pivotal Tracker and Git workflow'
|
7
|
+
|
8
|
+
version Pgit::VERSION
|
9
|
+
|
10
|
+
subcommand_option_handling :normal
|
11
|
+
arguments :strict
|
12
|
+
|
13
|
+
# desc 'Path to the config file'
|
14
|
+
# default_value "#{ENV['HOME']}/.pgit.rc.yml"
|
15
|
+
# arg_name '/path/to/.pivotal.yml'
|
16
|
+
# flag [:c,:config]
|
17
|
+
|
18
|
+
# desc 'Bypasses the app asking for confirmation'
|
19
|
+
# switch [:force]
|
20
|
+
|
21
|
+
# desc 'PivotalTracker API Token'
|
22
|
+
# arg_name 'secr3tT0ken123'
|
23
|
+
# flag [:'api-token']
|
24
|
+
|
25
|
+
desc "Installs the pgit configuration file"
|
26
|
+
command :install do |c|
|
27
|
+
c.action do |global_options,options,args|
|
28
|
+
PGit::Installer.new(global_options, options, args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# desc 'Start, finish, etc. Pivotal Tracker story branches'
|
33
|
+
desc 'Start Pivotal Tracker story branches'
|
34
|
+
long_desc "Makes a cURL request to Pivotal Tracker to fetch the story, based
|
35
|
+
on the given story id, parses the title, appends it with the story id. It then
|
36
|
+
creates a branch name with that parsed title and does a `git checkout` to it. "
|
37
|
+
|
38
|
+
command :story_branch do |c|
|
39
|
+
c.desc "Parses the title of the given Pivotal Tracker story,\n" +
|
40
|
+
" makes the branch name, and does a checkout"
|
41
|
+
c.arg_name 'STORY_ID'
|
42
|
+
c.flag :s, :start
|
43
|
+
|
44
|
+
# c.desc "Merge back to staging, remove local and remote (origin?) branches"
|
45
|
+
# c.switch :f, :finish
|
46
|
+
|
47
|
+
# c.desc "Attempts to join the story-branch that, presumably, already exists"
|
48
|
+
# c.arg_name 'STORY_ID'
|
49
|
+
# c.flag :j, :join
|
50
|
+
|
51
|
+
c.action do |global_options,options,args|
|
52
|
+
PGit::StoryBranch::Application.new(global_options, options, args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# desc 'Wraps `git commit -m` and prepends the message with story id of the branch'
|
57
|
+
|
58
|
+
# command :commit do |c|
|
59
|
+
# arg_name 'Describe arguments to commit here'
|
60
|
+
# c.action do |global_options,options,args|
|
61
|
+
# puts "commit command ran"
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
|
65
|
+
|
66
|
+
desc 'Initializes configuration file'
|
67
|
+
|
68
|
+
arg_name 'path/to/config_file'
|
69
|
+
|
70
|
+
# command :init_config do |c|
|
71
|
+
# c.action do |global_options,options,args|
|
72
|
+
# puts "init_config command ran"
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
|
76
|
+
pre do |global,command,options,args|
|
77
|
+
# Pre logic here
|
78
|
+
# Return true to proceed; false to abort and not call the
|
79
|
+
# chosen command
|
80
|
+
# Use skips_pre before a command to skip this block
|
81
|
+
# on that command only
|
82
|
+
true
|
83
|
+
end
|
84
|
+
|
85
|
+
post do |global,command,options,args|
|
86
|
+
# Post logic here
|
87
|
+
# Use skips_post before a command to skip this
|
88
|
+
# block on that command only
|
89
|
+
end
|
90
|
+
|
91
|
+
on_error do |exception|
|
92
|
+
# Error logic here
|
93
|
+
# return false to skip default error handling
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
exit run(ARGV)
|
data/lib/pgit.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pgit/configuration'
|
3
|
+
require 'pgit/current_branch'
|
4
|
+
require 'pgit/current_project'
|
5
|
+
require 'pgit/installer'
|
6
|
+
require 'pgit/name_parser'
|
7
|
+
require 'pgit/story'
|
8
|
+
require 'pgit/story_branch'
|
9
|
+
require 'pgit/story_branch/application'
|
10
|
+
require 'pgit/version.rb'
|
11
|
+
require 'yaml'
|
12
|
+
|
13
|
+
# Add requires for other files you add to your project here, so
|
14
|
+
# you just need to require this one file in your bin file
|
@@ -0,0 +1,77 @@
|
|
1
|
+
#
|
2
|
+
# Loads the Pivotal-Git configuration file
|
3
|
+
#
|
4
|
+
# - config_path is the path to the pivotal-git configuration file
|
5
|
+
# This loads the file. Throws an error if a key (such as api_token, path, id)
|
6
|
+
# is missing. Check #general_error_message for more info
|
7
|
+
#
|
8
|
+
|
9
|
+
module PGit
|
10
|
+
class Configuration
|
11
|
+
def self.default_options
|
12
|
+
{
|
13
|
+
'projects' => [
|
14
|
+
{
|
15
|
+
'api_token' => 'somepivotalatoken124',
|
16
|
+
'id' => '12345',
|
17
|
+
"path" => "~/some/path/to/a/pivotal-git/project"
|
18
|
+
},
|
19
|
+
{
|
20
|
+
'api_token' => 'somepivotalatoken124',
|
21
|
+
'id' => '23429070',
|
22
|
+
"path" => "~/some/other/pivotal-git/project"
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(config_path = '~/.pgit.rc.yml')
|
29
|
+
@expanded_path = File.expand_path(config_path)
|
30
|
+
if File.exists? @expanded_path
|
31
|
+
config_file = File.open(@expanded_path, 'r')
|
32
|
+
@yaml = YAML.load(config_file)
|
33
|
+
|
34
|
+
validate_existence_of_at_least_one_project
|
35
|
+
validate_presence_of_items_in_each_project
|
36
|
+
else
|
37
|
+
raise missing_config_default
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_yaml
|
42
|
+
@yaml
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def missing_config_default
|
48
|
+
"Default configuration file does not exist. Please run `pgit install`"
|
49
|
+
end
|
50
|
+
|
51
|
+
def general_error_message
|
52
|
+
"Please have the following layout:\n" + YAML.dump(PGit::Configuration.default_options)
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_presence_of_items_in_each_project
|
56
|
+
projects = @yaml["projects"]
|
57
|
+
all_present = projects.all? do |project|
|
58
|
+
project["api_token"] &&
|
59
|
+
project["path"] &&
|
60
|
+
project["id"]
|
61
|
+
end
|
62
|
+
|
63
|
+
unless all_present
|
64
|
+
raise "Error: Must have a path, id, and api_token for each project.\n" +
|
65
|
+
general_error_message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def validate_existence_of_at_least_one_project
|
70
|
+
unless @yaml["projects"]
|
71
|
+
raise "Error: #{@expanded_path} needs at least one project.\n" +
|
72
|
+
general_error_message
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PGit
|
2
|
+
class CurrentBranch
|
3
|
+
def initialize
|
4
|
+
@branches = `git branch`
|
5
|
+
|
6
|
+
raise @branches unless current.any?
|
7
|
+
end
|
8
|
+
|
9
|
+
def name
|
10
|
+
current.first.gsub(/\*\s*/, '')
|
11
|
+
end
|
12
|
+
|
13
|
+
def story_id
|
14
|
+
name.scan(/\d+$/).first
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def current
|
20
|
+
@branches.scan(/\*.+/)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#
|
2
|
+
# Decides what the "current project" is, in relation to the pwd.
|
3
|
+
#
|
4
|
+
# - config_yaml: has the project configurations. It has at least one
|
5
|
+
# project. Each project has an (pivotal) api_token, path, and (pivotal)
|
6
|
+
# id
|
7
|
+
#
|
8
|
+
|
9
|
+
module PGit
|
10
|
+
class CurrentProject
|
11
|
+
def initialize(config_yaml)
|
12
|
+
@current_project = find_current_project(config_yaml)
|
13
|
+
end
|
14
|
+
|
15
|
+
def pwd
|
16
|
+
project_path = @current_project["path"]
|
17
|
+
File.expand_path(project_path, __FILE__)
|
18
|
+
end
|
19
|
+
|
20
|
+
def id
|
21
|
+
@current_project["id"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def api_token
|
25
|
+
@current_project["api_token"]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def validate_pwd_match_at_least_one(matching_projects)
|
31
|
+
if matching_projects.length == 0
|
32
|
+
raise "None of the project paths matches the working directory"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def escape_slashes(project_path)
|
37
|
+
project_path.gsub('/','\/')
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_matching_projects(projects)
|
41
|
+
projects.select do |project|
|
42
|
+
project_path = project["path"]
|
43
|
+
extended_path = File.expand_path(project_path, __FILE__)
|
44
|
+
escaped_project = escape_slashes(extended_path)
|
45
|
+
Dir.pwd.match(/#{escaped_project}/)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_current_project(config_yaml)
|
50
|
+
projects = config_yaml["projects"]
|
51
|
+
matching_projects = find_matching_projects(projects)
|
52
|
+
|
53
|
+
validate_pwd_match_at_least_one(matching_projects)
|
54
|
+
find_best_match(matching_projects)
|
55
|
+
end
|
56
|
+
|
57
|
+
def find_best_match(matching_projects)
|
58
|
+
matching_projects.sort! { |a,b| b["path"].length <=> a["path"].length }
|
59
|
+
matching_projects.first
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module PGit
|
2
|
+
class Installer
|
3
|
+
def initialize(glob_opts, opts, args)
|
4
|
+
file_path = "~/.pgit.rc.yml"
|
5
|
+
@expanded_path = File.expand_path(file_path)
|
6
|
+
|
7
|
+
if File.exists? @expanded_path
|
8
|
+
raise "Error: #{file_path} already exists"
|
9
|
+
else
|
10
|
+
ask_continue
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def ask_continue
|
15
|
+
puts "*** Installing example pgit configuration file under ~/.pgit.rc.yml. Continue? [Y/n]"
|
16
|
+
if STDIN.gets.chomp.match(/y/i)
|
17
|
+
puts "Saving example pgit config in ~/.pgit.rc.yml..."
|
18
|
+
write_example_pgit_rc_file
|
19
|
+
else
|
20
|
+
puts "Aborting installation..."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def write_example_pgit_rc_file
|
25
|
+
File.open(@expanded_path, 'w') do |f|
|
26
|
+
YAML.dump(PGit::Configuration.default_options, f)
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "Saved! Please edit ~/.pgit.rc.yml and add the proper Pivotal Tracker API tokens, id, and file paths for each project"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#
|
2
|
+
# Parses pivotal story titles and turns them into git branch names
|
3
|
+
# - story.name: title of the pivotal tracker story
|
4
|
+
#
|
5
|
+
# - story_id: id of the pivotal tracker story
|
6
|
+
#
|
7
|
+
# ex: Fly so high in the sky 12345 becomes fly-so-high-sky-12345
|
8
|
+
module PGit
|
9
|
+
class StoryBranch
|
10
|
+
class NameParser
|
11
|
+
def initialize(story)
|
12
|
+
@story = story
|
13
|
+
@story_name = story.name
|
14
|
+
end
|
15
|
+
|
16
|
+
def story_id
|
17
|
+
@story.id
|
18
|
+
end
|
19
|
+
|
20
|
+
def name
|
21
|
+
remove_fluff_words
|
22
|
+
remove_non_alphanumeric_characters
|
23
|
+
downcase
|
24
|
+
add_story_id
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def remove_non_alphanumeric_characters
|
30
|
+
words = @story_name.split(' ')
|
31
|
+
split_words = words.each { |word| word.gsub!(/\W/, '') }
|
32
|
+
@story_name = split_words.select do |word|
|
33
|
+
!word.empty?
|
34
|
+
end.join('-')
|
35
|
+
end
|
36
|
+
|
37
|
+
def remove_fluff_words
|
38
|
+
fluff_words = %w{the on of}
|
39
|
+
fluff_words.each { |fluff_word| @story_name.gsub!(/\b#{fluff_word}\b/i, '') }
|
40
|
+
remove_extraneous_white_spaces
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove_extraneous_white_spaces
|
44
|
+
@story_name.strip!
|
45
|
+
@story_name.gsub!(/\s+/, ' ')
|
46
|
+
end
|
47
|
+
|
48
|
+
def downcase
|
49
|
+
@story_name.downcase!
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_story_id
|
53
|
+
"#{@story_name}-#{story_id}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|