story_branch 0.4.0 → 0.4.1
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/Gemfile.lock +8 -4
- data/lib/story_branch/git_utils.rb +12 -47
- data/lib/story_branch/git_wrapper.rb +108 -0
- data/lib/story_branch/main.rb +4 -3
- data/lib/story_branch/pivotal_utils.rb +13 -3
- data/lib/story_branch/version.rb +1 -1
- data/story_branch.gemspec +1 -1
- metadata +17 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d7d4d41d52f404121543545261d35ee4549bf4c
|
4
|
+
data.tar.gz: 501c4cbf920c86d5d3e161e232670b2e3f813570
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d41edd251bb11ec5adb61e0e852574f6d3bd27a7d475a00c91d025e8a6bed44f1c2d7356c169b0eb0c4eab864de1f270d3c410a357564f378b948b186e9526d7
|
7
|
+
data.tar.gz: 3d3016b1ae2ea18df7b6c7c5cf1730d7bb011567fd55eec5866698e4dfff8456d45a508fd3095a6d46ea864c194dbcaa21da359d06279332b1298a0f02b455d0
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
story_branch (0.4.
|
4
|
+
story_branch (0.4.1)
|
5
5
|
blanket_wrapper (~> 3.0)
|
6
|
-
git (~> 1.2)
|
7
6
|
levenshtein-ffi (~> 1.0)
|
8
7
|
pastel (~> 0.7.2)
|
9
8
|
thor (~> 0.20.0)
|
@@ -23,10 +22,14 @@ GEM
|
|
23
22
|
fakefs (0.18.0)
|
24
23
|
ffi (1.9.25)
|
25
24
|
git (1.5.0)
|
26
|
-
httparty (0.16.
|
25
|
+
httparty (0.16.3)
|
26
|
+
mime-types (~> 3.0)
|
27
27
|
multi_xml (>= 0.5.2)
|
28
28
|
levenshtein-ffi (1.1.0)
|
29
29
|
ffi (~> 1.9)
|
30
|
+
mime-types (3.2.2)
|
31
|
+
mime-types-data (~> 3.2015)
|
32
|
+
mime-types-data (3.2018.0812)
|
30
33
|
multi_xml (0.6.0)
|
31
34
|
necromancer (0.4.0)
|
32
35
|
pastel (0.7.2)
|
@@ -54,7 +57,7 @@ GEM
|
|
54
57
|
unicode-display_width (~> 1.4.0)
|
55
58
|
unicode_utils (~> 1.4.0)
|
56
59
|
strings-ansi (0.1.0)
|
57
|
-
thor (0.20.
|
60
|
+
thor (0.20.3)
|
58
61
|
timers (4.2.0)
|
59
62
|
tty-color (0.4.3)
|
60
63
|
tty-command (0.8.2)
|
@@ -87,6 +90,7 @@ PLATFORMS
|
|
87
90
|
DEPENDENCIES
|
88
91
|
bundler (~> 1.16)
|
89
92
|
fakefs (~> 0.14)
|
93
|
+
git (~> 1.5)
|
90
94
|
rake (~> 10.0)
|
91
95
|
rspec (~> 3)
|
92
96
|
rspec_junit_formatter (~> 0.4)
|
@@ -1,21 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'git'
|
4
3
|
require 'levenshtein'
|
4
|
+
require_relative './git_wrapper'
|
5
5
|
|
6
6
|
module StoryBranch
|
7
7
|
# Class used to interact with git. It relies on git gem as the wrapper
|
8
8
|
# and levenshtein algo to determine branch name proximity
|
9
9
|
class GitUtils
|
10
|
-
def self.g
|
11
|
-
::Git.open '.'
|
12
|
-
end
|
13
|
-
|
14
10
|
def self.existing_branch?(name)
|
15
|
-
branch_names.each do |n|
|
11
|
+
GitWrapper.branch_names.each do |n|
|
16
12
|
return true if Levenshtein.distance(n, name) < 3
|
17
|
-
|
13
|
+
|
14
|
+
branch_name_match = n.match(/(.*)(-[1-9]+[0-9]*$)/)
|
18
15
|
next unless branch_name_match
|
16
|
+
|
19
17
|
levenshtein_distance = Levenshtein.distance branch_name_match[1], name
|
20
18
|
return true if levenshtein_distance < 3
|
21
19
|
end
|
@@ -23,64 +21,31 @@ module StoryBranch
|
|
23
21
|
end
|
24
22
|
|
25
23
|
def self.branch_for_story_exists?(id)
|
26
|
-
branch_names.each do |n|
|
27
|
-
branch_id = n.match(/-[1-9][0-9]
|
24
|
+
GitWrapper.branch_names.each do |n|
|
25
|
+
branch_id = n.match(/-[1-9]+[0-9]*$/)
|
28
26
|
next unless branch_id
|
29
27
|
return true if branch_id.to_s == "-#{id}"
|
30
28
|
end
|
31
29
|
false
|
32
30
|
end
|
33
31
|
|
34
|
-
def self.branch_names
|
35
|
-
g.branches.map(&:name)
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.current_branch
|
39
|
-
g.current_branch
|
40
|
-
end
|
41
|
-
|
42
32
|
def self.current_story
|
43
|
-
/(.*)-(\d+$)/.match current_branch
|
33
|
+
/(.*)-(\d+$)/.match GitWrapper.current_branch
|
44
34
|
end
|
45
35
|
|
46
36
|
def self.current_branch_story_parts
|
47
37
|
matches = current_story
|
48
38
|
return {} unless matches.length == 3
|
49
|
-
{ title: matches[1], id: matches[2].to_i }
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.create_branch(name)
|
53
|
-
g.branch(name).create
|
54
|
-
g.branch(name).checkout
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.status_collect(status, regex)
|
58
|
-
chosen_stati = status.select { |e| e.match(regex) }
|
59
|
-
chosen_stati.map { |e| e.match(regex)[1] }
|
60
|
-
end
|
61
39
|
|
62
|
-
|
63
|
-
|
64
|
-
untracked_rx = /^\?\? (.*)/
|
65
|
-
staged_rx = /^M (.*)/
|
66
|
-
added_rx = /^A (.*)/
|
67
|
-
status = g.lib.send(:command, 'status', '-s').lines
|
68
|
-
return nil if status.empty?
|
69
|
-
{
|
70
|
-
modified: status_collect(status, modified_rx),
|
71
|
-
untracked: status_collect(status, untracked_rx),
|
72
|
-
added: status_collect(status, added_rx),
|
73
|
-
staged: status_collect(status, staged_rx)
|
74
|
-
}
|
40
|
+
title = matches[1].tr('-', ' ')
|
41
|
+
{ title: title, id: matches[2].to_i }
|
75
42
|
end
|
76
43
|
|
77
44
|
def self.status?(state)
|
45
|
+
status = GitWrapper.status
|
78
46
|
return false unless status
|
79
|
-
!status[state].empty?
|
80
|
-
end
|
81
47
|
|
82
|
-
|
83
|
-
g.commit(message)
|
48
|
+
!status[state].empty?
|
84
49
|
end
|
85
50
|
end
|
86
51
|
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# NOTE: Consider extracting this to a separate gem
|
4
|
+
module StoryBranch
|
5
|
+
# GitWrapper to help running git commands with direct system calls
|
6
|
+
# Essentially it provides a couple of commands to interact with git
|
7
|
+
# - StoryBranch::GitWrapper.command('<cmd>', [<opts>])
|
8
|
+
# Returns the output as is
|
9
|
+
#
|
10
|
+
# - StoryBranch::GitWrapper.command_lines('<cmd>', [<opts>])
|
11
|
+
# Returns the output split into an array of lines, stripped and chomped
|
12
|
+
#
|
13
|
+
# - StoryBranch::GitWrapper.branch_names
|
14
|
+
# Returns the list of available branch names, locally and configured remotes
|
15
|
+
class GitWrapper
|
16
|
+
STATI_MATCHERS = {
|
17
|
+
modified_rx: /^ M (.*)/,
|
18
|
+
untracked_rx: /^\?\? (.*)/,
|
19
|
+
staged_rx: /^M (.*)/,
|
20
|
+
added_rx: /^A (.*)/
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
def self.command(cmd, opts = [])
|
24
|
+
gw = new
|
25
|
+
gw.call(cmd, opts)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.command_lines(cmd, opts = [])
|
29
|
+
result = command(cmd, opts)
|
30
|
+
lines = result.split("\n")
|
31
|
+
lines.each(&:strip!)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.branch_names
|
35
|
+
# NOTE: Regex matcher for cases as:
|
36
|
+
# remotes/origin/allow.... <- remote branch (remove 'remotes/origin')
|
37
|
+
# * allow.... <- * indicates current branch (remove '* ')
|
38
|
+
# allow <- local branch (do nothing)
|
39
|
+
regex = %r{(^remotes\/.*\/|\s|[*])}
|
40
|
+
all_branches.map do |line|
|
41
|
+
line = line.sub(regex, '')
|
42
|
+
line
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.current_branch
|
47
|
+
current_branch_line = all_branches.detect do |line|
|
48
|
+
line.match(/\*/)
|
49
|
+
end
|
50
|
+
current_branch_line.tr('*', ' ')
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.all_branches
|
54
|
+
command_lines('branch', '-a')
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.create_branch(name)
|
58
|
+
command('checkout', ['-b', name])
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.status
|
62
|
+
g_status = command_lines('status', '-s')
|
63
|
+
return nil if g_status.empty?
|
64
|
+
|
65
|
+
{
|
66
|
+
modified: status_collect(g_status, STATI_MATCHERS[:modified_rx]),
|
67
|
+
untracked: status_collect(g_status, STATI_MATCHERS[:untracked_rx]),
|
68
|
+
added: status_collect(g_status, STATI_MATCHERS[:added_rx]),
|
69
|
+
staged: status_collect(g_status, STATI_MATCHERS[:staged_rx])
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.status_collect(status, regex)
|
74
|
+
chosen_stati = status.select { |e| e.match(regex) }
|
75
|
+
chosen_stati.map { |e| e.match(regex)[1] }
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.commit(message)
|
79
|
+
command('commit', ['-m', message])
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize
|
83
|
+
@system_git = 'git'
|
84
|
+
end
|
85
|
+
|
86
|
+
def call(cmd, opts = [])
|
87
|
+
opts = prepare_opts(opts)
|
88
|
+
git_cmd = "#{@system_git} #{cmd} #{opts}"
|
89
|
+
`#{git_cmd}`.chomp.strip
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# NOTE: Taken from ruby git gem
|
95
|
+
def escape(str = '')
|
96
|
+
str = str.to_s
|
97
|
+
return "'#{str.gsub('\'', '\'"\'"\'')}'" if RUBY_PLATFORM !~ /mingw|mswin/
|
98
|
+
|
99
|
+
# Keeping the old escape format for windows users
|
100
|
+
escaped = str.gsub('\'', '\'\\\'\'')
|
101
|
+
%("#{escaped}")
|
102
|
+
end
|
103
|
+
|
104
|
+
def prepare_opts(opts = [])
|
105
|
+
[opts].flatten.map { |s| escape(s) }.join(' ')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/story_branch/main.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative './pivotal_utils'
|
4
4
|
require_relative './git_utils'
|
5
|
+
require_relative './git_wrapper'
|
5
6
|
require_relative './config_manager'
|
6
7
|
require 'tty-prompt'
|
7
8
|
|
@@ -65,7 +66,7 @@ module StoryBranch
|
|
65
66
|
commit_message = "[#{finish_tag} ##{current_story[:id]}] #{current_story[:title]}"
|
66
67
|
proceed = prompt.yes?("Commit with standard message? #{commit_message}")
|
67
68
|
if proceed
|
68
|
-
|
69
|
+
GitWrapper.commit commit_message
|
69
70
|
else
|
70
71
|
prompt.say 'Aborted'
|
71
72
|
end
|
@@ -130,7 +131,7 @@ module StoryBranch
|
|
130
131
|
def create_feature_branch(story)
|
131
132
|
return if story.nil?
|
132
133
|
|
133
|
-
current_branch =
|
134
|
+
current_branch = GitWrapper.current_branch
|
134
135
|
prompt.say "You are checked out at: #{current_branch}"
|
135
136
|
branch_name = prompt.ask('Provide a new branch name',
|
136
137
|
default: story.dashed_title)
|
@@ -139,7 +140,7 @@ module StoryBranch
|
|
139
140
|
|
140
141
|
feature_branch_name_with_story_id = "#{feature_branch_name}-#{story.id}"
|
141
142
|
prompt.say("Creating: #{feature_branch_name_with_story_id} with #{current_branch} as parent")
|
142
|
-
|
143
|
+
GitWrapper.create_branch feature_branch_name_with_story_id
|
143
144
|
end
|
144
145
|
|
145
146
|
# Branch name validation
|
@@ -6,6 +6,7 @@ require_relative './string_utils'
|
|
6
6
|
module StoryBranch
|
7
7
|
# PivotalTracker Story representation
|
8
8
|
class Story
|
9
|
+
NON_ESTIMATED_TYPES = %w[chore bug release].freeze
|
9
10
|
attr_accessor :title, :id
|
10
11
|
|
11
12
|
def initialize(blanket_story, project)
|
@@ -13,6 +14,8 @@ module StoryBranch
|
|
13
14
|
@story = blanket_story
|
14
15
|
@title = blanket_story.name
|
15
16
|
@id = blanket_story.id
|
17
|
+
@story_type = blanket_story.story_type
|
18
|
+
@estimate = blanket_story.estimate
|
16
19
|
end
|
17
20
|
|
18
21
|
def update_state(new_state)
|
@@ -27,6 +30,11 @@ module StoryBranch
|
|
27
30
|
def dashed_title
|
28
31
|
StoryBranch::StringUtils.normalised_branch_name @title
|
29
32
|
end
|
33
|
+
|
34
|
+
def estimated
|
35
|
+
(@story_type == 'feature' && !@estimate.nil?) ||
|
36
|
+
NON_ESTIMATED_TYPES.include?(@story_type)
|
37
|
+
end
|
30
38
|
end
|
31
39
|
|
32
40
|
# PivotalTracker Project representation
|
@@ -37,17 +45,19 @@ module StoryBranch
|
|
37
45
|
|
38
46
|
# NOTE: takes in possible keys:
|
39
47
|
# - with_state
|
40
|
-
# - estimated
|
41
48
|
# Returns an array of PT Stories (Story Class)
|
42
49
|
# TODO: add other possible args
|
43
|
-
def stories(options = {})
|
50
|
+
def stories(options = {}, estimated = true)
|
44
51
|
stories = if options[:id]
|
45
52
|
[@project.stories(options[:id])]
|
46
53
|
else
|
47
54
|
params = { with_state: options[:with_state] }
|
48
55
|
@project.stories.get(params: params).payload
|
49
56
|
end
|
50
|
-
stories.map { |s| Story.new(s, @project) }
|
57
|
+
stories = stories.map { |s| Story.new(s, @project) }
|
58
|
+
return stories if estimated == false
|
59
|
+
|
60
|
+
stories.select(&:estimated)
|
51
61
|
end
|
52
62
|
end
|
53
63
|
|
data/lib/story_branch/version.rb
CHANGED
data/story_branch.gemspec
CHANGED
@@ -47,7 +47,6 @@ Gem::Specification.new do |spec|
|
|
47
47
|
spec.require_paths = ['lib']
|
48
48
|
|
49
49
|
spec.add_runtime_dependency 'blanket_wrapper', '~> 3.0'
|
50
|
-
spec.add_runtime_dependency 'git', '~> 1.2'
|
51
50
|
spec.add_runtime_dependency 'levenshtein-ffi', '~> 1.0'
|
52
51
|
spec.add_runtime_dependency 'pastel', '~> 0.7.2'
|
53
52
|
spec.add_runtime_dependency 'thor', '~> 0.20.0'
|
@@ -58,6 +57,7 @@ Gem::Specification.new do |spec|
|
|
58
57
|
|
59
58
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
60
59
|
spec.add_development_dependency 'fakefs', '~> 0.14'
|
60
|
+
spec.add_development_dependency 'git', '~> 1.5'
|
61
61
|
spec.add_development_dependency 'rake', '~> 10.0'
|
62
62
|
spec.add_development_dependency 'rspec', '~> 3'
|
63
63
|
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.4'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: story_branch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rui Baltazar
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: exe
|
14
14
|
cert_chain: []
|
15
|
-
date: 2018-11-
|
15
|
+
date: 2018-11-23 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: blanket_wrapper
|
@@ -28,20 +28,6 @@ dependencies:
|
|
28
28
|
- - "~>"
|
29
29
|
- !ruby/object:Gem::Version
|
30
30
|
version: '3.0'
|
31
|
-
- !ruby/object:Gem::Dependency
|
32
|
-
name: git
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
34
|
-
requirements:
|
35
|
-
- - "~>"
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: '1.2'
|
38
|
-
type: :runtime
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
requirements:
|
42
|
-
- - "~>"
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
version: '1.2'
|
45
31
|
- !ruby/object:Gem::Dependency
|
46
32
|
name: levenshtein-ffi
|
47
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -168,6 +154,20 @@ dependencies:
|
|
168
154
|
- - "~>"
|
169
155
|
- !ruby/object:Gem::Version
|
170
156
|
version: '0.14'
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
name: git
|
159
|
+
requirement: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - "~>"
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '1.5'
|
164
|
+
type: :development
|
165
|
+
prerelease: false
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - "~>"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '1.5'
|
171
171
|
- !ruby/object:Gem::Dependency
|
172
172
|
name: rake
|
173
173
|
requirement: !ruby/object:Gem::Requirement
|
@@ -260,6 +260,7 @@ files:
|
|
260
260
|
- lib/story_branch/commands/unstart.rb
|
261
261
|
- lib/story_branch/config_manager.rb
|
262
262
|
- lib/story_branch/git_utils.rb
|
263
|
+
- lib/story_branch/git_wrapper.rb
|
263
264
|
- lib/story_branch/main.rb
|
264
265
|
- lib/story_branch/pivotal_utils.rb
|
265
266
|
- lib/story_branch/string_utils.rb
|