jiragit 0.5.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +112 -0
- data/Rakefile +18 -0
- data/bin/jiragit +5 -0
- data/hooks/commit-msg +90 -0
- data/hooks/post-checkout +107 -0
- data/hooks/post-commit +51 -0
- data/hooks/prepare-commit-msg +144 -0
- data/lib/jiragit.rb +10 -0
- data/lib/jiragit/cli.rb +249 -0
- data/lib/jiragit/configuration.rb +51 -0
- data/lib/jiragit/git/branch.rb +29 -0
- data/lib/jiragit/git/commit_response.rb +23 -0
- data/lib/jiragit/git/repository.rb +222 -0
- data/lib/jiragit/jira_store.rb +48 -0
- data/lib/jiragit/logger.rb +32 -0
- data/lib/jiragit/tag.rb +30 -0
- data/lib/jiragit/vault.rb +113 -0
- data/lib/jiragit/version.rb +3 -0
- data/spec/branch_spec.rb +105 -0
- data/spec/cli_context.rb +13 -0
- data/spec/cli_spec.rb +149 -0
- data/spec/commit_message_spec.rb +82 -0
- data/spec/commit_spec.rb +81 -0
- data/spec/configuration_spec.rb +32 -0
- data/spec/git_editor.rb +40 -0
- data/spec/git_editor_spec.rb +59 -0
- data/spec/jira_store_spec.rb +57 -0
- data/spec/logger_spec.rb +37 -0
- data/spec/merge_spec.rb +85 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/tag_spec.rb +35 -0
- data/spec/test_repository_context.rb +17 -0
- data/spec/test_support.rb +81 -0
- data/spec/vault_spec.rb +100 -0
- metadata +124 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Jiragit
|
4
|
+
|
5
|
+
class JiraStore
|
6
|
+
|
7
|
+
def initialize(location = "#{Dir.home}/.jira_store")
|
8
|
+
self.location = location
|
9
|
+
end
|
10
|
+
|
11
|
+
def relate(params)
|
12
|
+
tags = extract_tags(params).compact
|
13
|
+
vault.relate(*tags)
|
14
|
+
vault.save
|
15
|
+
end
|
16
|
+
|
17
|
+
def relations(params)
|
18
|
+
jira, branch, commit = extract_tags(params).compact.first
|
19
|
+
tag = [jira, branch, commit].detect { |tag| !tag.nil? }
|
20
|
+
return Set.new unless tag
|
21
|
+
vault.load
|
22
|
+
vault.relations(tag)
|
23
|
+
end
|
24
|
+
|
25
|
+
def reload
|
26
|
+
vault.load
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
attr_accessor :location
|
32
|
+
attr_reader :vault
|
33
|
+
|
34
|
+
def vault
|
35
|
+
@vault ||= Vault.new(location)
|
36
|
+
end
|
37
|
+
|
38
|
+
def extract_tags(params)
|
39
|
+
jira = branch = commit = nil
|
40
|
+
jira = Tag.new(jira: params[:jira]) if params.include?(:jira)
|
41
|
+
branch = Tag.new(branch: params[:branch]) if params.include?(:branch)
|
42
|
+
commit = Tag.new(commit: params[:commit]) if params.include?(:commit)
|
43
|
+
[jira, branch, commit]
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Jiragit
|
5
|
+
|
6
|
+
class Logger < ::Logger
|
7
|
+
|
8
|
+
attr_accessor :hook
|
9
|
+
|
10
|
+
def initialize(path = nil)
|
11
|
+
if path
|
12
|
+
@path = path
|
13
|
+
else
|
14
|
+
@path = ".git/jiragit/jiragit.log"
|
15
|
+
end
|
16
|
+
FileUtils.mkdir_p directory
|
17
|
+
super(@path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def format_message(severity, datetime, progname, msg)
|
21
|
+
"#{datetime} #{@hook}: #{msg}\n"
|
22
|
+
end
|
23
|
+
|
24
|
+
def directory
|
25
|
+
dir = @path.gsub(/(^|\/)[^\/]*?$/,'')
|
26
|
+
dir = "." if dir == ''
|
27
|
+
dir
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/jiragit/tag.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
module Jiragit
|
2
|
+
|
3
|
+
class Tag
|
4
|
+
|
5
|
+
attr_accessor :type, :label
|
6
|
+
|
7
|
+
def initialize(hashtag)
|
8
|
+
@type = hashtag.first.first.to_sym
|
9
|
+
@label = hashtag.first.last.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{type}: #{label}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def inspect
|
17
|
+
to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash
|
21
|
+
to_s.hash
|
22
|
+
end
|
23
|
+
|
24
|
+
def eql?(other)
|
25
|
+
other.to_s.hash == to_s.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Jiragit
|
2
|
+
|
3
|
+
class Vault
|
4
|
+
|
5
|
+
def initialize(location)
|
6
|
+
self.location = location
|
7
|
+
load_or_create
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_or_create
|
11
|
+
self.vault = load || create
|
12
|
+
end
|
13
|
+
|
14
|
+
def load
|
15
|
+
return false unless File.exists?(location)
|
16
|
+
self.vault = Marshal.load(File.read(location))
|
17
|
+
end
|
18
|
+
|
19
|
+
def create
|
20
|
+
self.vault = {}
|
21
|
+
save
|
22
|
+
vault
|
23
|
+
end
|
24
|
+
|
25
|
+
def save
|
26
|
+
File.open(location, 'w+') { |file| file.write Marshal.dump(vault) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(item)
|
30
|
+
vault[item] = vault[item]
|
31
|
+
end
|
32
|
+
|
33
|
+
def include?(item)
|
34
|
+
vault.keys.include?(item)
|
35
|
+
end
|
36
|
+
|
37
|
+
def relate(*items)
|
38
|
+
items.each do |item|
|
39
|
+
add(item) unless include?(item)
|
40
|
+
items.each do |related_item|
|
41
|
+
add(related_item) unless include?(related_item)
|
42
|
+
update_relation(item, related_item) unless item == related_item
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def relations(item)
|
48
|
+
vault[item] || Set.new
|
49
|
+
end
|
50
|
+
|
51
|
+
def related?(*items)
|
52
|
+
items.inject(true) do |related, item|
|
53
|
+
related && items.inject(true) do |related, related_item|
|
54
|
+
related && related_items?(item, related_item)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def distally_related?(item, related_item)
|
60
|
+
!!relation_chain(item, related_item)
|
61
|
+
end
|
62
|
+
|
63
|
+
def relation_chain(item, related_item)
|
64
|
+
visited = Set.new
|
65
|
+
visited << item
|
66
|
+
to_visit = [ [ [item] , relations(item).to_a ] ]
|
67
|
+
loop do
|
68
|
+
break unless to_visit.any?
|
69
|
+
chain, items = to_visit.first
|
70
|
+
if items.any?
|
71
|
+
item = items.shift
|
72
|
+
if !visited.include?(item)
|
73
|
+
visited << item
|
74
|
+
relations = relations(item)
|
75
|
+
to_visit << [chain + [item], relations.to_a] if relations.any?
|
76
|
+
return chain + [item] if item == related_item
|
77
|
+
end
|
78
|
+
else
|
79
|
+
to_visit.shift
|
80
|
+
end
|
81
|
+
end
|
82
|
+
false
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
attr_accessor :vault
|
88
|
+
attr_accessor :location
|
89
|
+
|
90
|
+
def related_items?(item, related_item)
|
91
|
+
case
|
92
|
+
when item == related_item
|
93
|
+
true
|
94
|
+
when relations(item).include?(related_item)
|
95
|
+
true
|
96
|
+
else
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_relation(item, related_item)
|
102
|
+
relations = relations(item)
|
103
|
+
relations << related_item
|
104
|
+
vault[item] = relations if relations_empty?(item)
|
105
|
+
end
|
106
|
+
|
107
|
+
def relations_empty?(item)
|
108
|
+
self.include?(item) || vault[item].nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
data/spec/branch_spec.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
require 'expect'
|
3
|
+
|
4
|
+
describe "Repository Branching Behaviors" do
|
5
|
+
|
6
|
+
include_context "Test Repository"
|
7
|
+
|
8
|
+
context "when checking out a new branch" do
|
9
|
+
|
10
|
+
it "asks for a jira number" do
|
11
|
+
checkout_a_new_branch('new_branch', 'PA-12345')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "runs the post-checkout hook" do
|
15
|
+
checkout_a_new_branch('new_branch', 'PA-12345')
|
16
|
+
assert_log_contains(/post-checkout/)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "records a jira_branch relation" do
|
20
|
+
checkout_a_new_branch('new_branch', 'PA-12345')
|
21
|
+
assert_relation({jira: 'PA-12345'}, {branch: 'new_branch'})
|
22
|
+
end
|
23
|
+
|
24
|
+
it "records multiple jira_branch relations" do
|
25
|
+
checkout_a_new_branch('new_branch', 'PA-12345, PA-54321')
|
26
|
+
assert_relation({jira: 'PA-12345'}, {branch: 'new_branch'})
|
27
|
+
assert_relation({jira: 'PA-54321'}, {branch: 'new_branch'})
|
28
|
+
end
|
29
|
+
|
30
|
+
context "previous branch has associated jiras" do
|
31
|
+
|
32
|
+
before do
|
33
|
+
checkout_a_new_branch('parent_branch', 'PA-12345')
|
34
|
+
@repo.make_a_commit
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should provide and accept defaults" do
|
38
|
+
assert_relation({jira: 'PA-12345'}, {branch: 'parent_branch'})
|
39
|
+
checkout_a_new_branch_with_default('child_branch')
|
40
|
+
assert_relation({jira: 'PA-12345'}, {branch: 'child_branch'})
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when checking out an existing branch" do
|
48
|
+
|
49
|
+
context "without an associated jira" do
|
50
|
+
|
51
|
+
before do
|
52
|
+
checkout_a_new_branch('new_branch')
|
53
|
+
@repo.make_a_commit
|
54
|
+
checkout_an_existing_branch('master')
|
55
|
+
end
|
56
|
+
|
57
|
+
it "asks for a jira number" do
|
58
|
+
checkout_an_existing_branch('new_branch', 'PA-12345')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "runs the post-checkout hook" do
|
62
|
+
checkout_an_existing_branch('new_branch', 'PA-12345')
|
63
|
+
assert_log_contains(/post-checkout/)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "has no prexisting jira_branch relation" do
|
67
|
+
assert_no_relation({jira: 'PA-12345'}, {branch: 'new_branch'})
|
68
|
+
end
|
69
|
+
|
70
|
+
it "records a jira_branch relation" do
|
71
|
+
checkout_an_existing_branch('new_branch', 'PA-12345')
|
72
|
+
assert_relation({jira: 'PA-12345'}, {branch: 'new_branch'})
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
context "with an associated jira" do
|
78
|
+
|
79
|
+
before do
|
80
|
+
checkout_a_new_branch('new_branch', 'PA-12345')
|
81
|
+
@repo.make_a_commit
|
82
|
+
checkout_an_existing_branch('master')
|
83
|
+
end
|
84
|
+
|
85
|
+
it "does not ask for a jira number" do
|
86
|
+
@repo.checkout_branch('new_branch') do |output, input|
|
87
|
+
output.expect("What is the JIRA Number?", 5) do |message|
|
88
|
+
expect(message).to be nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
it "lists associated jira numbers" do
|
94
|
+
@repo.checkout_branch('new_branch') do |output, input|
|
95
|
+
output.expect("PA-12345", 5) do |message|
|
96
|
+
expect(message).to_not be nil
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
data/spec/cli_context.rb
ADDED
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
module Jiragit
|
4
|
+
|
5
|
+
describe Cli do
|
6
|
+
|
7
|
+
include_context "CLI input/output"
|
8
|
+
|
9
|
+
it "should provide help with no command line options" do
|
10
|
+
Cli.new([])
|
11
|
+
expect($stdout.string).to match(/Commands/)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "in an empty repository" do
|
15
|
+
|
16
|
+
before do
|
17
|
+
@current_directory = Dir.pwd
|
18
|
+
@directory = "/tmp/test_directory"
|
19
|
+
Dir.mkdir(@directory) unless Dir.exists?(@directory)
|
20
|
+
Dir.chdir(@directory)
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
Dir.chdir(@current_directory)
|
25
|
+
Dir.rmdir(@directory)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "provides an error message when calling commands that require a repository" do
|
29
|
+
Cli.new([:install])
|
30
|
+
expect($stderr.string).to match(/No valid Git repository/)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
context "in an empty repository" do
|
36
|
+
|
37
|
+
before do
|
38
|
+
@current_directory = Dir.pwd
|
39
|
+
repository = "test_repository"
|
40
|
+
Jiragit::Git::Repository.create(repository)
|
41
|
+
Dir.chdir repository
|
42
|
+
end
|
43
|
+
|
44
|
+
after do
|
45
|
+
Dir.chdir(@current_directory)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should not have hooks installed" do
|
49
|
+
cli = Cli.new([])
|
50
|
+
expect(Dir.exists?(".git")).to be true
|
51
|
+
expect(Dir.exists?(".git/hooks")).to be true
|
52
|
+
cli.send(:gem_hook_files).each do |hook|
|
53
|
+
expect(File.exists?(".git/hooks/#{hook}")).to be false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should install hooks" do
|
58
|
+
cli = Cli.new([:install])
|
59
|
+
cli.send(:gem_hook_files).each do |hook|
|
60
|
+
expect(File.exists?(".git/hooks/#{hook}")).to be true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should uninstalled hooks" do
|
65
|
+
cli = Cli.new([:install])
|
66
|
+
cli = Cli.new([:uninstall])
|
67
|
+
cli.send(:gem_hook_files).each do |hook|
|
68
|
+
expect(File.exists?(".git/hooks/#{hook}")).to be false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "in a test repository" do
|
75
|
+
|
76
|
+
include_context "Test Repository"
|
77
|
+
|
78
|
+
context "in a new feature branch" do
|
79
|
+
|
80
|
+
before do
|
81
|
+
@repo.create('a_file', 'The quick fox jumped over the fence')
|
82
|
+
@repo.add('a_file')
|
83
|
+
@repo.commit('initial commit')
|
84
|
+
checkout_a_new_branch('feature_branch', 'PA-12345')
|
85
|
+
@repo.create('a_file', 'The brown fox jumped over the fence')
|
86
|
+
@repo.add('a_file')
|
87
|
+
@repo.commit('feature commit')
|
88
|
+
Dir.chdir @repository
|
89
|
+
end
|
90
|
+
|
91
|
+
after do
|
92
|
+
Dir.chdir '..'
|
93
|
+
end
|
94
|
+
|
95
|
+
it "browses to the current branch by default" do
|
96
|
+
expect_any_instance_of(Cli).to receive(:run) do |instance, arg|
|
97
|
+
expect(arg).to match(/open/)
|
98
|
+
expect(arg).to match(/feature_branch/)
|
99
|
+
end
|
100
|
+
cli = Cli.new([:browse])
|
101
|
+
end
|
102
|
+
|
103
|
+
it "browses to the current jira" do
|
104
|
+
expect_any_instance_of(Cli).to receive(:run) do |instance, arg|
|
105
|
+
expect(arg).to match(/open/)
|
106
|
+
expect(arg).to match(/PA-12345/)
|
107
|
+
end
|
108
|
+
cli = Cli.new([:browse, 'jira'])
|
109
|
+
end
|
110
|
+
|
111
|
+
it "browses to the current branch" do
|
112
|
+
expect_any_instance_of(Cli).to receive(:run) do |instance, arg|
|
113
|
+
expect(arg).to match(/open/)
|
114
|
+
expect(arg).to match(/feature_branch/)
|
115
|
+
end
|
116
|
+
cli = Cli.new([:browse, 'branch'])
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
context "in a new feature branch without a jira" do
|
122
|
+
|
123
|
+
before do
|
124
|
+
@repo.create('a_file', 'The quick fox jumped over the fence')
|
125
|
+
@repo.add('a_file')
|
126
|
+
@repo.commit('initial commit')
|
127
|
+
checkout_a_new_branch_with_default('feature_branch')
|
128
|
+
@repo.create('a_file', 'The brown fox jumped over the fence')
|
129
|
+
@repo.add('a_file')
|
130
|
+
@repo.commit('feature commit')
|
131
|
+
Dir.chdir @repository
|
132
|
+
end
|
133
|
+
|
134
|
+
after do
|
135
|
+
Dir.chdir '..'
|
136
|
+
end
|
137
|
+
|
138
|
+
it "does not browse to an unspecified jira" do
|
139
|
+
expect_any_instance_of(Cli).to_not receive(:run)
|
140
|
+
cli = Cli.new([:browse, 'jira'])
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|