git-duet 0.1.3 → 0.2.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/.rspec +1 -0
- data/.rubocop.yml +3 -0
- data/.ruby-version +1 -0
- data/.simplecov +1 -0
- data/.travis.yml +4 -6
- data/Gemfile +1 -0
- data/LICENSE +1 -1
- data/README.md +1 -0
- data/Rakefile +7 -1
- data/bin/git-duet +2 -1
- data/bin/git-duet-commit +2 -1
- data/bin/git-duet-install-hook +2 -1
- data/bin/git-duet-pre-commit +2 -1
- data/bin/git-solo +2 -1
- data/git-duet.gemspec +5 -7
- data/lib/git-duet.rb +1 -0
- data/lib/git/duet.rb +4 -4
- data/lib/git/duet/author_mapper.rb +14 -10
- data/lib/git/duet/cli.rb +22 -26
- data/lib/git/duet/command_methods.rb +44 -12
- data/lib/git/duet/commit_command.rb +20 -15
- data/lib/git/duet/config.rb +11 -0
- data/lib/git/duet/duet_command.rb +11 -5
- data/lib/git/duet/install_hook_command.rb +14 -10
- data/lib/git/duet/pre_commit_command.rb +11 -8
- data/lib/git/duet/script_die_error.rb +2 -0
- data/lib/git/duet/solo_command.rb +21 -7
- data/lib/git/duet/version.rb +3 -1
- data/spec/integration/end_to_end_spec.rb +74 -54
- data/spec/lib/git/duet/author_mapper_spec.rb +45 -42
- data/spec/lib/git/duet/cli_spec.rb +20 -19
- data/spec/lib/git/duet/command_methods_spec.rb +21 -11
- data/spec/lib/git/duet/duet_command_spec.rb +75 -51
- data/spec/lib/git/duet/pre_commit_command_spec.rb +19 -14
- data/spec/lib/git/duet/solo_command_spec.rb +118 -73
- data/spec/spec_helper.rb +3 -16
- data/spec/support/author_mapper_helper.rb +22 -18
- metadata +25 -41
- data/.rbenv-version +0 -1
- data/bin/git-duet-pre-commit-tk +0 -3
- data/lib/git/duet/key_error.rb +0 -3
@@ -1,3 +1,4 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
1
2
|
require 'git/duet/author_mapper'
|
2
3
|
|
3
4
|
describe Git::Duet::AuthorMapper do
|
@@ -22,83 +23,85 @@ describe Git::Duet::AuthorMapper do
|
|
22
23
|
ENV.delete('GIT_DUET_AUTHORS_FILE')
|
23
24
|
end
|
24
25
|
|
25
|
-
it '
|
26
|
-
instance = described_class
|
27
|
-
|
26
|
+
it 'uses an authors file given at initialization' do
|
27
|
+
instance = described_class
|
28
|
+
.new('/blarggie/blarggie/new/friend/.git-authors')
|
29
|
+
instance.authors_file
|
30
|
+
.should == '/blarggie/blarggie/new/friend/.git-authors'
|
28
31
|
end
|
29
32
|
|
30
|
-
it '
|
33
|
+
it 'uses the `GIT_DUET_AUTHORS_FILE` if provided' do
|
31
34
|
ENV['GIT_DUET_AUTHORS_FILE'] = '/fizzle/bizzle/.git-authors'
|
32
35
|
instance = described_class.new
|
33
36
|
instance.authors_file.should == '/fizzle/bizzle/.git-authors'
|
34
37
|
end
|
35
38
|
|
36
|
-
it '
|
39
|
+
it 'falls back to using `~/.git-authors` for the author map' do
|
37
40
|
subject.authors_file.should == File.join(ENV['HOME'], '.git-authors')
|
38
41
|
end
|
39
42
|
|
40
|
-
it '
|
43
|
+
it 'lets missing author errors bubble up' do
|
41
44
|
expect { subject.map('bzzzrt') }.to raise_error
|
42
45
|
end
|
43
46
|
|
44
|
-
it '
|
47
|
+
it 'maps initials to name -> email pairs' do
|
45
48
|
subject.map('jd').fetch('jd').should == {
|
46
|
-
:
|
47
|
-
:
|
49
|
+
name: 'Jane Doe',
|
50
|
+
email: 'jane@awesome.biz'
|
48
51
|
}
|
49
52
|
end
|
50
53
|
|
51
|
-
it '
|
54
|
+
it 'constructs default emails from first initial and last name + domain' do
|
52
55
|
subject.map('hb').should == {
|
53
56
|
'hb' => {
|
54
|
-
:
|
55
|
-
:
|
57
|
+
name: 'Hampton Bones',
|
58
|
+
email: 'h.bones@awesometown.me'
|
56
59
|
}
|
57
60
|
}
|
58
61
|
end
|
59
62
|
|
60
|
-
it '
|
63
|
+
it 'constructs emails from optional username (if given) + domain' do
|
61
64
|
subject.map('fb').should == {
|
62
65
|
'fb' => {
|
63
|
-
:
|
64
|
-
:
|
66
|
+
name: 'Frances Bar',
|
67
|
+
email: 'frances@awesometown.me'
|
65
68
|
}
|
66
69
|
}
|
67
70
|
end
|
68
71
|
|
69
|
-
it '
|
72
|
+
it 'uses an explicitly-configured email address if given' do
|
70
73
|
subject.map('jd').should == {
|
71
74
|
'jd' => {
|
72
|
-
:
|
73
|
-
:
|
75
|
+
name: 'Jane Doe',
|
76
|
+
email: 'jane@awesome.biz'
|
74
77
|
}
|
75
78
|
}
|
76
79
|
end
|
77
80
|
|
78
|
-
it '
|
81
|
+
it 'maps any number of initials to name -> email pairs' do
|
79
82
|
subject.map('jd', 'fb', 'qx', 'hb').should == {
|
80
83
|
'jd' => {
|
81
|
-
:
|
82
|
-
:
|
84
|
+
name: 'Jane Doe',
|
85
|
+
email: 'jane@awesome.biz'
|
83
86
|
},
|
84
87
|
'fb' => {
|
85
|
-
:
|
86
|
-
:
|
88
|
+
name: 'Frances Bar',
|
89
|
+
email: 'frances@awesometown.me'
|
87
90
|
},
|
88
91
|
'qx' => {
|
89
|
-
:
|
90
|
-
:
|
92
|
+
name: 'Quincy Xavier',
|
93
|
+
email: 'qx@awesometown.me'
|
91
94
|
},
|
92
95
|
'hb' => {
|
93
|
-
:
|
94
|
-
:
|
96
|
+
name: 'Hampton Bones',
|
97
|
+
email: 'h.bones@awesometown.me'
|
95
98
|
}
|
96
99
|
}
|
97
100
|
end
|
98
101
|
|
99
102
|
context 'when using a `~/.pairs` config' do
|
100
103
|
before :each do
|
101
|
-
subject.stub(:
|
104
|
+
subject.stub(cfg: {
|
102
105
|
'pairs' => {
|
103
106
|
'jd' => 'Jane Doe; jdoe',
|
104
107
|
'fb' => 'Frances Bar; frances',
|
@@ -114,30 +117,30 @@ describe Git::Duet::AuthorMapper do
|
|
114
117
|
})
|
115
118
|
end
|
116
119
|
|
117
|
-
it '
|
120
|
+
it 'maps initials to name -> email pairs' do
|
118
121
|
subject.map('jd').fetch('jd').should == {
|
119
|
-
:
|
120
|
-
:
|
122
|
+
name: 'Jane Doe',
|
123
|
+
email: 'jane@awesome.biz'
|
121
124
|
}
|
122
125
|
end
|
123
126
|
|
124
|
-
it '
|
127
|
+
it 'maps any number of initials to name -> email pairs' do
|
125
128
|
subject.map('jd', 'fb', 'qx', 'hb').should == {
|
126
129
|
'jd' => {
|
127
|
-
:
|
128
|
-
:
|
130
|
+
name: 'Jane Doe',
|
131
|
+
email: 'jane@awesome.biz'
|
129
132
|
},
|
130
133
|
'fb' => {
|
131
|
-
:
|
132
|
-
:
|
134
|
+
name: 'Frances Bar',
|
135
|
+
email: 'frances@awesometown.me'
|
133
136
|
},
|
134
137
|
'qx' => {
|
135
|
-
:
|
136
|
-
:
|
138
|
+
name: 'Quincy Xavier',
|
139
|
+
email: 'qx@awesometown.me'
|
137
140
|
},
|
138
141
|
'hb' => {
|
139
|
-
:
|
140
|
-
:
|
142
|
+
name: 'Hampton Bones',
|
143
|
+
email: 'h.bones@awesometown.me'
|
141
144
|
}
|
142
145
|
}
|
143
146
|
end
|
@@ -159,14 +162,14 @@ describe Git::Duet::AuthorMapper do
|
|
159
162
|
)
|
160
163
|
end
|
161
164
|
|
162
|
-
it '
|
165
|
+
it 'warns about missing authors file' do
|
163
166
|
$stderr.should_receive(:puts).with(
|
164
167
|
/Missing or corrupt authors file.*#{bad_path}/i
|
165
168
|
)
|
166
169
|
expect { subject.map('zzz') }.to raise_error
|
167
170
|
end
|
168
171
|
|
169
|
-
it '
|
172
|
+
it 'raises a ScriptDieError' do
|
170
173
|
$stderr.stub(:puts)
|
171
174
|
expect { subject.map('zzz') }.to raise_error(Git::Duet::ScriptDieError)
|
172
175
|
end
|
@@ -1,46 +1,47 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
1
2
|
require 'git/duet/cli'
|
2
3
|
require 'git/duet/solo_command'
|
3
4
|
require 'git/duet/duet_command'
|
4
5
|
require 'git/duet/pre_commit_command'
|
5
6
|
|
6
7
|
describe Git::Duet::Cli do
|
7
|
-
subject { described_class }
|
8
|
+
subject(:cli) { described_class }
|
8
9
|
|
9
|
-
it '
|
10
|
-
|
10
|
+
it 'responds to `.main`' do
|
11
|
+
cli.should respond_to(:run)
|
11
12
|
end
|
12
13
|
|
13
|
-
it '
|
14
|
-
expect {
|
14
|
+
it 'requires the prog name and argv array' do
|
15
|
+
expect { cli.run }.to raise_error(ArgumentError)
|
15
16
|
end
|
16
17
|
|
17
|
-
it '
|
18
|
-
expect {
|
18
|
+
it 'explodes on unknown prog names' do
|
19
|
+
expect { cli.run('bork', []) }.to raise_error(ScriptError)
|
19
20
|
end
|
20
21
|
|
21
|
-
it '
|
22
|
-
|
23
|
-
|
22
|
+
it 'returns the exit status from any script error deaths' do
|
23
|
+
cli.stub(:solo).and_raise(Git::Duet::ScriptDieError.new(99))
|
24
|
+
cli.run('git-solo', %w(ty -q)).should == 99
|
24
25
|
end
|
25
26
|
|
26
|
-
it '
|
27
|
-
Git::Duet::SoloCommand.stub(:
|
27
|
+
it 'runs `solo` when the progname matches /solo$/' do
|
28
|
+
Git::Duet::SoloCommand.stub(new: double('solo').tap do |solo|
|
28
29
|
solo.should_receive(:execute!)
|
29
30
|
end)
|
30
|
-
|
31
|
+
cli.run('git-solo', %w(jd -q))
|
31
32
|
end
|
32
33
|
|
33
|
-
it '
|
34
|
-
Git::Duet::DuetCommand.stub(:
|
34
|
+
it 'runs `duet` when progname matches /duet$/' do
|
35
|
+
Git::Duet::DuetCommand.stub(new: double('duet').tap do |duet|
|
35
36
|
duet.should_receive(:execute!)
|
36
37
|
end)
|
37
|
-
|
38
|
+
cli.run('git-duet', %w(jd fb -q))
|
38
39
|
end
|
39
40
|
|
40
|
-
it '
|
41
|
-
Git::Duet::PreCommitCommand.stub(:
|
41
|
+
it 'runs `pre_commit` when progname matches /pre-commit$/' do
|
42
|
+
Git::Duet::PreCommitCommand.stub(new: double('pre-commit').tap do |pc|
|
42
43
|
pc.should_receive(:execute!)
|
43
44
|
end)
|
44
|
-
|
45
|
+
cli.run('git-duet-pre-commit', %w(-q))
|
45
46
|
end
|
46
47
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
1
2
|
require 'git/duet/command_methods'
|
2
3
|
|
3
4
|
describe Git::Duet::CommandMethods do
|
@@ -23,17 +24,21 @@ describe Git::Duet::CommandMethods do
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
it '
|
27
|
-
subject.should_receive(:`)
|
28
|
-
|
29
|
-
subject.should_receive(:`)
|
27
|
+
it 'writes env vars to a custom git config tree' do
|
28
|
+
subject.should_receive(:`)
|
29
|
+
.with("git config #{Git::Duet::Config.namespace}.fizzle-baz 'awesome'")
|
30
|
+
subject.should_receive(:`)
|
31
|
+
.with("git config #{Git::Duet::Config.namespace}.oh-snarf 'mumra'")
|
32
|
+
subject.should_receive(:`)
|
33
|
+
.with(/^git config #{Git::Duet::Config.namespace}.mtime \d+/)
|
30
34
|
subject.send(:write_env_vars)
|
31
35
|
end
|
32
36
|
|
33
|
-
it '
|
37
|
+
it 'explodes if a subshell returns non-zero' do
|
34
38
|
subject.stub(:`)
|
35
|
-
|
36
|
-
expect { subject.send(:exec_check, 'ls hamsters') }
|
39
|
+
$CHILD_STATUS.should_receive(:exitstatus).and_return(1)
|
40
|
+
expect { subject.send(:exec_check, 'ls hamsters') }
|
41
|
+
.to raise_error(StandardError)
|
37
42
|
end
|
38
43
|
|
39
44
|
context 'when configured to operate on the global config' do
|
@@ -41,10 +46,15 @@ describe Git::Duet::CommandMethods do
|
|
41
46
|
subject.instance_variable_set(:@global, true)
|
42
47
|
end
|
43
48
|
|
44
|
-
it '
|
45
|
-
subject.should_receive(:`)
|
46
|
-
|
47
|
-
|
49
|
+
it 'writes env vars to a custom global git config tree' do
|
50
|
+
subject.should_receive(:`)
|
51
|
+
.with("git config --global #{Git::Duet::Config.namespace}" <<
|
52
|
+
".fizzle-baz 'awesome'")
|
53
|
+
subject.should_receive(:`)
|
54
|
+
.with("git config --global #{Git::Duet::Config.namespace}" <<
|
55
|
+
".oh-snarf 'mumra'")
|
56
|
+
subject.should_receive(:`)
|
57
|
+
.with(/^git config --global #{Git::Duet::Config.namespace}.mtime \d+/)
|
48
58
|
subject.send(:write_env_vars)
|
49
59
|
end
|
50
60
|
end
|
@@ -1,71 +1,70 @@
|
|
1
|
+
# vim:fileencoding=utf-8
|
1
2
|
require 'git/duet/duet_command'
|
2
3
|
require 'support/author_mapper_helper'
|
3
4
|
|
4
5
|
describe Git::Duet::DuetCommand do
|
5
6
|
include SpecSupport::AuthorMapperHelper
|
6
7
|
|
7
|
-
let
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
let :omega do
|
12
|
-
random_author
|
13
|
-
end
|
14
|
-
|
15
|
-
subject do
|
16
|
-
described_class.new(alpha, omega)
|
17
|
-
end
|
8
|
+
let(:alpha) { random_author }
|
9
|
+
let(:omega) { random_author }
|
10
|
+
subject(:cmd) { described_class.new(alpha, omega) }
|
18
11
|
|
19
12
|
before :each do
|
20
|
-
|
21
|
-
am.stub(:
|
13
|
+
cmd.stub(author_mapper: double('author mapper').tap do |am|
|
14
|
+
am.stub(map: author_mapping)
|
22
15
|
end)
|
23
|
-
|
24
|
-
|
16
|
+
cmd.stub(:` => '')
|
17
|
+
cmd.stub(:report_env_vars)
|
25
18
|
Dir.stub(:chdir) do |&block|
|
26
19
|
block.call
|
27
20
|
end
|
28
|
-
File.stub(:open) do |filename,mode
|
21
|
+
File.stub(:open) do |filename, mode, &block|
|
29
22
|
block.call(double('outfile').as_null_object)
|
30
23
|
end
|
31
24
|
end
|
32
25
|
|
33
|
-
it '
|
26
|
+
it 'requires alpha and omega sets of initials' do
|
34
27
|
expect { described_class.new }.to raise_error(ArgumentError)
|
35
28
|
end
|
36
29
|
|
37
|
-
it '
|
38
|
-
|
30
|
+
it 'responds to `execute!`' do
|
31
|
+
cmd.should respond_to(:execute!)
|
39
32
|
end
|
40
33
|
|
41
|
-
it '
|
42
|
-
|
34
|
+
it '(privately) responds to `write_env_vars`' do
|
35
|
+
cmd.private_methods.map(&:to_sym).should include(:write_env_vars)
|
43
36
|
end
|
44
37
|
|
45
|
-
it '
|
46
|
-
|
47
|
-
|
48
|
-
|
38
|
+
it 'sets the alpha name as git config user.name' do
|
39
|
+
cmd.stub(:`).with(/git config user\.email/)
|
40
|
+
cmd.should_receive(:`)
|
41
|
+
.with("git config user.name '#{author_mapping[alpha][:name]}'")
|
42
|
+
cmd.execute!
|
49
43
|
end
|
50
44
|
|
51
|
-
it '
|
52
|
-
|
53
|
-
|
54
|
-
|
45
|
+
it 'sets the alpha email as git config user.email' do
|
46
|
+
cmd.stub(:`).with(/git config user\.name/)
|
47
|
+
cmd.should_receive(:`)
|
48
|
+
.with("git config user.email '#{author_mapping[alpha][:email]}'")
|
49
|
+
cmd.execute!
|
55
50
|
end
|
56
51
|
|
57
|
-
it '
|
58
|
-
|
59
|
-
$stdout.should_receive(:puts)
|
60
|
-
|
61
|
-
$stdout.should_receive(:puts)
|
62
|
-
|
63
|
-
|
52
|
+
it 'reports env vars to $stdout' do
|
53
|
+
cmd.unstub(:report_env_vars)
|
54
|
+
$stdout.should_receive(:puts)
|
55
|
+
.with(/^GIT_AUTHOR_NAME='#{author_mapping[alpha][:name]}'/)
|
56
|
+
$stdout.should_receive(:puts)
|
57
|
+
.with(/^GIT_AUTHOR_EMAIL='#{author_mapping[alpha][:email]}'/)
|
58
|
+
$stdout.should_receive(:puts)
|
59
|
+
.with(/^GIT_COMMITTER_NAME='#{author_mapping[omega][:name]}'/)
|
60
|
+
$stdout.should_receive(:puts)
|
61
|
+
.with(/^GIT_COMMITTER_EMAIL='#{author_mapping[omega][:email]}'/)
|
62
|
+
cmd.execute!
|
64
63
|
end
|
65
64
|
|
66
|
-
it '
|
67
|
-
|
68
|
-
|
65
|
+
it 'sets the alpha as author and omega as committer in custom git config' do
|
66
|
+
cmd.should_receive(:write_env_vars)
|
67
|
+
cmd.execute!
|
69
68
|
end
|
70
69
|
|
71
70
|
%w(alpha omega).each do |author_type|
|
@@ -73,27 +72,52 @@ describe Git::Duet::DuetCommand do
|
|
73
72
|
let(:"#{author_type}") { 'brzzzt' }
|
74
73
|
|
75
74
|
it 'aborts' do
|
76
|
-
|
77
|
-
expect {
|
75
|
+
cmd.stub(error: nil)
|
76
|
+
expect { cmd.execute! }.to raise_error(Git::Duet::ScriptDieError)
|
78
77
|
end
|
79
78
|
end
|
80
79
|
end
|
81
80
|
|
82
81
|
context 'when configured to operate on the global config' do
|
83
|
-
subject
|
84
|
-
|
82
|
+
subject(:cmd) { described_class.new(alpha, omega, false, true) }
|
83
|
+
|
84
|
+
it 'sets the alpha name as global git config user.name' do
|
85
|
+
cmd.stub(:`).with(/git config --global user\.email/)
|
86
|
+
alpha_name = author_mapping[alpha][:name]
|
87
|
+
cmd.should_receive(:`)
|
88
|
+
.with("git config --global user.name '#{alpha_name}'")
|
89
|
+
cmd.execute!
|
85
90
|
end
|
86
91
|
|
87
|
-
it '
|
88
|
-
|
89
|
-
|
90
|
-
|
92
|
+
it 'sets the alpha email as global git config user.email' do
|
93
|
+
cmd.stub(:`).with(/git config --global user\.name/)
|
94
|
+
alpha_email = author_mapping[alpha][:email]
|
95
|
+
cmd.should_receive(:`)
|
96
|
+
.with("git config --global user.email '#{alpha_email}'")
|
97
|
+
cmd.execute!
|
91
98
|
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context 'when given no arguments' do
|
102
|
+
let(:alpha) { nil }
|
103
|
+
let(:omega) { nil }
|
104
|
+
|
105
|
+
it 'shows the current duet author settings' do
|
106
|
+
git_config_output = <<-EOF.gsub(/^ {8}/, '')
|
107
|
+
#{Git::Duet::Config.namespace}.git-author-name Test Author
|
108
|
+
#{Git::Duet::Config.namespace}.git-author-email author@test.com
|
109
|
+
#{Git::Duet::Config.namespace}.git-committer-name Test Committer
|
110
|
+
#{Git::Duet::Config.namespace}.git-committer-email committer@test.com
|
111
|
+
#{Git::Duet::Config.namespace}.mtime 138039#{rand(1000..9999)}
|
112
|
+
EOF
|
113
|
+
|
114
|
+
cmd.stub(:`)
|
115
|
+
.with("git config --get-regexp #{Git::Duet::Config.namespace}") do
|
116
|
+
git_config_output
|
117
|
+
end
|
118
|
+
$stdout.should_receive(:puts).with(git_config_output)
|
92
119
|
|
93
|
-
|
94
|
-
subject.stub(:`).with(/git config --global user\.name/)
|
95
|
-
subject.should_receive(:`).with("git config --global user.email '#{author_mapping[alpha][:email]}'")
|
96
|
-
subject.execute!
|
120
|
+
cmd.execute!
|
97
121
|
end
|
98
122
|
end
|
99
123
|
end
|