pivotal_git_scripts_3 3.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 354565aac90f300622fb17b2076096c9248bab6cdf17bad09968b2ea04e4dfd6
4
+ data.tar.gz: e3742432925e0d7bc1735072aec99c91cba867690ed30adbd105e6553161fbcf
5
+ SHA512:
6
+ metadata.gz: 983c0495e8a566dde1b566f21c5d2f7df43289bb378fd00cc6e8f607e63aa1dd1e1ab82967ca41d1cc617585da3162dd2709871945fa42fd5ea8f126ef30ffb5
7
+ data.tar.gz: 650f6c6c69e2f696fa0355681f582554d2f4418b79b7e7457535b760b1e0a0e613f16f6cfdd5d187ef323b6b73c7d85218a52c605ea52d4bc66f764f702ffbc0
data/CHANGELOG.md ADDED
@@ -0,0 +1,26 @@
1
+ # CHANGELOG
2
+
3
+ ## 1.4.0 - released 2015-01-15
4
+
5
+ ### `git pair-commit` explicitly sets the author name
6
+
7
+ Before this change, git's user.name setting would override. In
8
+ repositories where the user had previously run `git duet`, this meant
9
+ that after a `git pair --global ...` the email was set correctly by `git
10
+ pair-commit`, but the author name was not.
11
+
12
+ [#62606550]
13
+
14
+ ### `git pair-commit` sets committer name and email
15
+
16
+ [Finishes #62606550]
17
+
18
+ ### Add ability to use a custom email address per user
19
+
20
+ # include the following section to set custom email addresses for users
21
+ email_addresses:
22
+ zr: zach.robinson@example.com
23
+
24
+ ### Include the $HOME directory if it's not in the list of pwd ancestors
25
+
26
+ This fixes the actual behavior to match the documentation, which states that the `.pairs` file may be in the user's home directory.
data/MIT.LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Pivotal Labs
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,104 @@
1
+ [![Build Status](https://travis-ci.org/pivotal/git_scripts.png)](https://travis-ci.org/pivotal/git_scripts)
2
+
3
+ # Git Scripts
4
+
5
+ These scripts are helpers for managing developer workflow when using git repos hosted on GitHub. Install as a rubygem and they can be run as standard git commands like `git about`.
6
+
7
+ ## Gem Installation
8
+
9
+ gem install pivotal_git_scripts
10
+
11
+ ## System Wide Installation
12
+
13
+ cd /usr/local/bin && curl -L http://github.com/pivotal/git_scripts/tarball/master | gunzip | tar xvf - --strip=2
14
+
15
+ ## git-about
16
+
17
+ `git about` shows settings set by `git pair` and `git project`
18
+
19
+ ## git-pair
20
+
21
+ Configures git authors when pair programming.
22
+
23
+ git pair sp js
24
+ user.name=Josh Susser & Sam Pierson
25
+ user.email=pair+jsusser+sam@pivotallabs.com
26
+
27
+
28
+ Create a `.pairs` config file in project root or your home folder.
29
+
30
+ # .pairs - configuration for 'git pair'
31
+ pairs:
32
+ # <initials>: <Firstname> <Lastname>[; <email-id>]
33
+ eh: Edward Hieatt
34
+ js: Josh Susser; jsusser
35
+ sf: Serguei Filimonov; serguei
36
+ email:
37
+ prefix: pair
38
+ domain: pivotallabs.com
39
+ no_solo_prefix: true
40
+ global: false # Set to true for git-pair to change git configuration for all your projects
41
+
42
+
43
+ By default this affects the current project (`.git/config`).
44
+ Use the `--global` option or add `global: true` to your `.pairs` file to set the global git configuration for all projects (`~/.gitconfig`).
45
+
46
+ Options are:
47
+
48
+ -g, --global Modify global git options instead of local
49
+ -v, --version Show Version
50
+ -h, --help Show this.
51
+
52
+ When you're done pairing, change git's configuration to use your personal details.
53
+
54
+ git pair <your-initials>
55
+
56
+ ## git-pair-commit
57
+
58
+ Makes a git commit as normal, but chooses one of the pair members randomly to get credit for the commit on github (by setting the author email to that member's email address). The author name on the commit will list all members of the pair, as usual.
59
+
60
+ If pair members have email addresses on different domains, you can specify them separately in your `.pairs` file.
61
+
62
+ pairs:
63
+ jd: Jane Doe
64
+ fb: Frances Bar
65
+ email_addresses:
66
+ jd: jane@awesome.local
67
+ fb: frances@foo.bar
68
+
69
+ ### Using git-pair-commit in RubyMine
70
+ RubyMine already supports pointing at a custom location for your git executable in the Preferences -> Version Control -> Git
71
+ ![screen shot 2014-03-11 at 12 49 02 pm](https://f.cloud.github.com/assets/163532/2390097/49c3023e-a956-11e3-8aeb-dcba1a814309.png)
72
+ The trick then is that `pair-commit` doesn't encompass all git functionality, so you can't just point RubyMine directly at it, you need something in the middle that will use `pair-commit` if the first arg is `commit`, otherwise just pass through. Here's a ruby script to do just that:
73
+ ```ruby
74
+ #!/usr/bin/env ruby
75
+
76
+ exit_code = if ARGV[1] == "commit"
77
+ system "git pair-commit #{ARGV[1..-1].join(" ")}"
78
+ else
79
+ system "git #{ARGV.join(" ")}"
80
+ end
81
+
82
+ exit exit_code
83
+ ```
84
+ Make sure it's executable.
85
+
86
+ ## git-project
87
+
88
+ $ git project pivots
89
+
90
+ This script sets the user account you will use to access repos hosted on github.com. It creates a symlink from `id_github_current` to `id_github_pivotal<project>`, which switches the SSH key you are currently using to access GitHub repos. Make sure you have the following lines in your .ssh/config file:
91
+
92
+ Host github.com
93
+ User git
94
+ IdentityFile /Users/pivotal/.ssh/id_github_current
95
+
96
+ Authors
97
+ ====
98
+ Copyright (c) 2010 [Pivotal Labs](http://pivotallabs.com). This software is licensed under the MIT License.
99
+
100
+ ### [Contributors](https://github.com/pivotal/git_scripts/contributors)
101
+ - git-pair original author [Bryan Helmkamp](http://brynary.com)
102
+ - lots of pivots :)
103
+ - [James Sanders](https://github.com/jsanders)
104
+ - [Todd Persen](https://github.com/toddboom)
data/bin/git-about ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ user_name = `git config --get user.name`
6
+ user_name = 'NONE' if user_name.strip.empty?
7
+
8
+ user_email = `git config --get user.email`
9
+ user_email = 'NONE' if user_email.strip.empty?
10
+
11
+ begin
12
+ key_file = File.readlink(File.expand_path('~/.ssh/id_github_current'))
13
+ project = (key_file =~ /id_github_(\w+)/ ? Regexp.last_match(1) : 'NONE')
14
+ rescue Errno::ENOENT
15
+ project = 'NONE'
16
+ end
17
+
18
+ puts "git user: #{user_name}"
19
+ puts "git email: #{user_email}"
20
+ puts "GitHub project: #{project}"
data/bin/git-pair ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) # this is required when running from /usr/local/bin
6
+ require 'pivotal_git_scripts/git_pair'
7
+
8
+ PivotalGitScripts::GitPair.main(ARGV)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__)) # this is required when running from /usr/local/bin
6
+ require 'pivotal_git_scripts/git_pair'
7
+
8
+ PivotalGitScripts::GitPair.commit(ARGV)
data/bin/git-project ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require 'socket'
6
+
7
+ def check_ssh_config
8
+ return unless `grep -c id_github_current config`.to_i.zero?
9
+
10
+ puts <<-WARNING
11
+ You must edit your .ssh/config file to include:
12
+ Host github.com
13
+ User git
14
+ IdentityFile /Users/pivotal/.ssh/id_github_current
15
+ WARNING
16
+ end
17
+
18
+ project = ARGV[0] || Dir.pwd.split('/').last
19
+ hostname = Socket.gethostname.split('.').first
20
+ possible_id_files = [
21
+ "id_#{hostname}_github_pivotal#{project}",
22
+ "id_github_pivotal#{project}",
23
+ "id_github_#{project}"
24
+ ]
25
+
26
+ Dir.chdir(File.expand_path('~/.ssh')) do
27
+ possible_id_files.each do |id_file|
28
+ next unless File.exist?(id_file)
29
+
30
+ `ln -sf #{id_file} id_github_current`
31
+ puts "Now using key #{id_file}"
32
+ check_ssh_config
33
+ exit 0
34
+ end
35
+ puts "Key not found. You must create a ssh key in ~/.ssh/ called #{possible_id_files[0..-2].join(', ')} or #{possible_id_files[-1]}"
36
+ end
data/bin/git-superpull ADDED
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+
3
+ git pull && git submodule init && git submodule update
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pivotal_git_scripts/version'
4
+ require 'yaml'
5
+ require 'optparse'
6
+ require 'pathname'
7
+
8
+ module PivotalGitScripts
9
+ module GitPair
10
+ def self.main(argv)
11
+ runner = Runner.new
12
+ runner.main(argv)
13
+ end
14
+
15
+ def self.commit(argv)
16
+ runner = Runner.new
17
+ runner.commit(argv)
18
+ end
19
+
20
+ class GitPairException < Exception; end
21
+
22
+ class Runner
23
+ def main(argv)
24
+ git_dir = `git rev-parse --git-dir`.chomp
25
+ exit 1 if git_dir.empty?
26
+
27
+ options = parse_cli_options(argv)
28
+ initials = argv
29
+ config = read_pairs_config
30
+ global = !!(options[:global] || config['global'])
31
+
32
+ if initials.any?
33
+ author_names, email_ids = extract_author_names_and_email_ids_from_config(config, initials)
34
+ authors = pair_names(author_names)
35
+ git_config = { name: authors, initials: initials.sort.join(' ') }
36
+ git_config[:email] = build_email(email_ids, config['email']) unless no_email(config)
37
+ set_git_config global, git_config
38
+ else
39
+ git_config = { name: nil, initials: nil }
40
+ git_config[:email] = nil unless no_email(config)
41
+ set_git_config global, git_config
42
+ puts "Unset#{' global' if global} user.name, #{'user.email, ' unless no_email(config)}user.initials"
43
+ end
44
+
45
+ %i[name email initials].each do |key|
46
+ report_git_settings(git_dir, key)
47
+ end
48
+ rescue GitPairException => e
49
+ puts e.message
50
+ exit 1
51
+ end
52
+
53
+ def commit(argv)
54
+ if argv[0] == '-h'
55
+ puts 'Usage: git pair-commit [options_for_git_commit]'
56
+ puts ''
57
+ puts 'Commits changes to the repository using `git commit`, but randomly chooses the author email from the'
58
+ puts 'members of the pair. In order for GitHub to assign credit for the commit activity, the user\'s email'
59
+ puts 'must be linked in their GitHub account.'
60
+ exit 0
61
+ end
62
+
63
+ config = read_pairs_config
64
+ author_details = extract_author_details_from_config(config, current_pair_initials)
65
+ author_names = author_details.keys.map { |i| author_details[i][:name] }
66
+ authors = pair_names(author_names)
67
+ author_email = random_author_email(author_details)
68
+ puts "Committing under #{author_email}"
69
+ passthrough_args = argv.map { |arg| "'#{arg}'" }.join(' ')
70
+ env_variables = "GIT_AUTHOR_NAME='#{authors}' GIT_AUTHOR_EMAIL='#{author_email}' GIT_COMMITTER_NAME='#{authors}' GIT_COMMITTER_EMAIL='#{author_email}'"
71
+ system "#{env_variables} git commit #{passthrough_args}"
72
+ rescue GitPairException => e
73
+ puts e.message
74
+ exit 1
75
+ end
76
+
77
+ def current_pair_initials
78
+ initials = `git config user.initials`.strip.split(' ')
79
+ raise GitPairException, 'Error: No pair set. Please set your pair with `git pair ...`' if initials.empty?
80
+
81
+ initials
82
+ end
83
+
84
+ def parse_cli_options(argv)
85
+ options = {}
86
+ OptionParser.new do |opts|
87
+ # copy-paste from readme
88
+ opts.banner = <<BANNER.sub('<br/>', '')
89
+ Configures git authors when pair programming.
90
+
91
+ git pair sp js
92
+ user.name=Josh Susser and Sam Pierson
93
+ user.email=pair+jsusser+sam@pivotallabs.com
94
+
95
+
96
+ Create a `.pairs` config file in project root or your home folder.
97
+
98
+ # .pairs - configuration for 'git pair'
99
+ pairs:
100
+ # <initials>: <Firstname> <Lastname>[; <email-id>]
101
+ eh: Edward Hieatt
102
+ js: Josh Susser; jsusser
103
+ sf: Serguei Filimonov; serguei
104
+ # if email section is present, email will be set
105
+ # if you leave out the email config section, email will not be set
106
+ email:
107
+ prefix: pair
108
+ domain: pivotallabs.com
109
+ # no_solo_prefix: true
110
+ #global: true
111
+ # include the following section to set custom email addresses for users
112
+ #email_addresses:
113
+ # zr: zach.robinson@example.com
114
+
115
+
116
+ By default this affects the current project (.git/config).<br/>
117
+ Use the `--global` option or add `global: true` to your `.pairs` file to set the global git configuration for all projects (~/.gitconfig).
118
+
119
+ Options are:
120
+ BANNER
121
+ opts.on('-g', '--global', 'Modify global git options instead of local') { options[:global] = true }
122
+ opts.on('-v', '--version', 'Show Version') do
123
+ puts PivotalGitScripts::VERSION
124
+ exit
125
+ end
126
+ opts.on('-h', '--help', 'Show this.') do
127
+ puts opts
128
+ exit
129
+ end
130
+ end.parse!(argv)
131
+
132
+ options
133
+ end
134
+
135
+ def read_pairs_config
136
+ pairs_file_name = '.pairs'
137
+
138
+ directory = File.absolute_path(Dir.pwd)
139
+ candidate_directories = [directory]
140
+ until Pathname.new(directory).root?
141
+ directory = File.absolute_path(File.join(directory, '..'))
142
+ candidate_directories << directory
143
+ end
144
+ home = File.absolute_path(ENV['HOME'])
145
+ candidate_directories << home unless candidate_directories.include? home
146
+
147
+ pairs_file_path = candidate_directories
148
+ .map { |d| File.join(d, '.pairs') }
149
+ .find { |f| File.exist? f }
150
+
151
+ unless pairs_file_path
152
+ raise GitPairException, <<-INSTRUCTIONS
153
+ Could not find a .pairs file. Create a YAML file in your project or home directory.
154
+ Format: <initials>: <name>[; <email>]
155
+ Example:
156
+ # .pairs - configuration for 'git pair'
157
+ # place in project or home directory
158
+ pairs:
159
+ eh: Edward Hieatt
160
+ js: Josh Susser; jsusser
161
+ sf: Serguei Filimonov; serguei
162
+ email:
163
+ prefix: pair
164
+ domain: pivotallabs.com
165
+ INSTRUCTIONS
166
+ end
167
+
168
+ YAML.load_file(pairs_file_path)
169
+ end
170
+
171
+ def read_author_info_from_config(config, initials_ary)
172
+ initials_ary.map do |initials|
173
+ config['pairs'][initials.downcase] or
174
+ raise GitPairException,
175
+ "Couldn't find author name for initials: #{initials}. Add this person to the .pairs file in your project or home directory."
176
+ end
177
+ end
178
+
179
+ def build_email(emails, config)
180
+ if config.is_a?(Hash)
181
+ prefix = config['prefix'] if !config['no_solo_prefix'] or emails.size > 1
182
+ "#{([prefix] + emails).compact.join('+')}@#{config['domain']}"
183
+ else
184
+ config
185
+ end
186
+ end
187
+
188
+ def random_author_email(author_details)
189
+ author_id = author_details.keys.sample
190
+ author_details[author_id][:email]
191
+ end
192
+
193
+ def set_git_config(global, options)
194
+ options.each do |key, value|
195
+ config_key = "user.#{key}"
196
+ arg = value ? %(#{config_key} "#{value}") : "--unset #{config_key}"
197
+ system(%(git config#{' --global' if global} #{arg}))
198
+ end
199
+ end
200
+
201
+ def report_git_settings(git_dir, key)
202
+ global = `git config --global --get-regexp '^user\.#{key}'`
203
+ local = `git config -f #{git_dir}/config --get-regexp '^user\.#{key}'`
204
+ puts "NOTE: Overriding global user.#{key} setting with local." if global.length > 0 && local.length > 0
205
+ puts "global: #{global}" if global.length > 0
206
+ puts "local: #{local}" if local.length > 0
207
+ end
208
+
209
+ def extract_author_names_and_email_ids_from_config(config, initials)
210
+ authors = read_author_info_from_config(config, initials)
211
+ authors.sort!.uniq! # FIXME
212
+ authors.map do |a|
213
+ full_name, email_id = a.split(';').map(&:strip)
214
+ email_id ||= full_name.split(' ').first.downcase
215
+ [full_name, email_id]
216
+ end.transpose
217
+ end
218
+
219
+ def no_email(config)
220
+ !config.key? 'email'
221
+ end
222
+
223
+ def extract_author_details_from_config(config, initials)
224
+ details = {}
225
+
226
+ initials.each do |i|
227
+ info = read_author_info_from_config(config, [i]).first
228
+
229
+ full_name, email_id = info.split(';').map(&:strip)
230
+ email_id ||= full_name.split(' ').first.downcase
231
+
232
+ email = read_custom_email_address_from_config(config, i)
233
+ email ||= "#{email_id}@#{config['email']['domain']}"
234
+
235
+ details[i] = {
236
+ name: full_name,
237
+ email: email
238
+ }
239
+ end
240
+
241
+ details
242
+ end
243
+
244
+ def read_custom_email_address_from_config(config, initial)
245
+ return nil unless config['email_addresses']
246
+
247
+ config['email_addresses'][initial.downcase]
248
+ end
249
+
250
+ private
251
+
252
+ def pair_names(author_names)
253
+ [author_names[0..-2].join(', '), author_names.last].reject(&:empty?).join(' and ')
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PivotalGitScripts
4
+ VERSION = '3.0.0'
5
+ end
data/spec/cli_spec.rb ADDED
@@ -0,0 +1,449 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'unindent'
4
+
5
+ describe 'CLI' do
6
+ before :all do
7
+ # use local scripts
8
+ ENV['PATH'] = "#{File.join(File.dirname(__FILE__), '..', 'bin')}:#{ENV['PATH']}"
9
+ end
10
+
11
+ def run(command, options = {})
12
+ output = `#{command}`
13
+ return output if $?.success?
14
+ return output if options[:fail]
15
+
16
+ message = "Unable to run #{command.inspect} in #{Dir.pwd}.\n#{output}"
17
+ warn "ERROR: #{message}"
18
+ raise message
19
+ end
20
+
21
+ def write(file, content)
22
+ File.open(file, 'w') { |f| f.write content }
23
+ end
24
+
25
+ around do |example|
26
+ dir = 'spec/tmp'
27
+ run "rm -rf #{dir}"
28
+ run "mkdir #{dir}"
29
+
30
+ # use fake home for .ssh hacks
31
+ run "mkdir #{dir}/home"
32
+ ENV['HOME'] = File.absolute_path("#{dir}/home")
33
+
34
+ Dir.chdir dir do
35
+ run 'touch a'
36
+ run 'git init'
37
+ run 'git add .'
38
+ run "git config user.email 'rspec-tests@example.com'"
39
+ run "git config user.name 'rspec test suite'"
40
+ run "git commit -am 'initial'"
41
+ run 'git config --unset user.email'
42
+ run 'git config --unset user.name'
43
+ example.run
44
+ end
45
+ end
46
+
47
+ describe 'about' do
48
+ it 'lists the user' do
49
+ run 'git config user.name NAME'
50
+ run('git about').should =~ /git user:\s+NAME/
51
+ end
52
+
53
+ it 'lists the user as NONE if there is none' do
54
+ run "git config user.name ''"
55
+ run('git about').should =~ /git user:\s+NONE/
56
+ end
57
+
58
+ it 'lists the email' do
59
+ run 'git config user.email EMAIL'
60
+ run('git about').should =~ /git email:\s+EMAIL/
61
+ end
62
+
63
+ it 'lists the email as NONE if there is none' do
64
+ run "git config user.email ''"
65
+ run('git about').should =~ /git email:\s+NONE/
66
+ end
67
+
68
+ it 'does not find a project' do
69
+ run('git about').should =~ /GitHub project:\s+NONE/
70
+ end
71
+
72
+ context 'with github project' do
73
+ before do
74
+ run 'mkdir home/.ssh'
75
+ run 'touch home/.ssh/id_github_foo'
76
+ run 'ln -s home/.ssh/id_github_foo home/.ssh/id_github_current'
77
+ end
78
+
79
+ it 'finds a project' do
80
+ run('git about').should =~ /GitHub project:\s+foo/
81
+ end
82
+ end
83
+ end
84
+
85
+ describe 'pair' do
86
+ def expect_config(result, name, initials, email, options = {})
87
+ global = 'cd /tmp && ' if options[:global]
88
+ run("#{global}git config user.name").should == "#{name}\n"
89
+ run("#{global}git config user.initials").should == "#{initials}\n"
90
+ run("#{global}git config user.email").should == "#{email}\n"
91
+
92
+ prefix = (options[:global] ? 'global: ' : 'local: ')
93
+ result.should include "#{prefix}user.name #{name}"
94
+ result.should include "#{prefix}user.initials #{initials}"
95
+ result.should include "#{prefix}user.email #{email}"
96
+ end
97
+
98
+ def git_config_value(name, global = false)
99
+ global_prefix = 'cd /tmp && ' if global
100
+ `#{global_prefix}git config user.#{name}`
101
+ end
102
+
103
+ it 'prints help' do
104
+ result = run 'git-pair --help'
105
+ result.should include('Configures git authors when pair programming')
106
+ end
107
+
108
+ it 'prints version' do
109
+ result = run 'git pair --version'
110
+ result.should =~ /\d+\.\d+\.\d+/
111
+ end
112
+
113
+ context 'with .pairs file' do
114
+ before do
115
+ write '.pairs', <<-YAML.unindent
116
+ pairs:
117
+ ab: Aa Bb
118
+ bc: Bb Cc
119
+ cd: Cc Dd
120
+
121
+ email:
122
+ prefix: the-pair
123
+ domain: the-host.com
124
+ YAML
125
+ end
126
+
127
+ describe 'global' do
128
+ it 'sets pairs globally when global: true is set' do
129
+ write '.pairs', File.read('.pairs') + "\nglobal: true"
130
+ result = run 'git pair ab'
131
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com', global: true
132
+ end
133
+
134
+ it 'sets pairs globally when --global is given' do
135
+ result = run 'git pair ab --global'
136
+ result.should include 'global: user.name Aa Bb'
137
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com', global: true
138
+ end
139
+
140
+ it 'unsets global config when no argument is passed' do
141
+ run 'git pair ab --global'
142
+ run 'git pair ab'
143
+ result = run 'git pair --global'
144
+ # result.should include "Unset --global user.name, user.email and user.initials"
145
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com'
146
+ result.should_not include('global:')
147
+ end
148
+ end
149
+
150
+ it 'can set a single user as pair' do
151
+ result = run 'git pair ab'
152
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com'
153
+ end
154
+
155
+ it 'can set a 2 users as pair' do
156
+ result = run 'git pair ab bc'
157
+ expect_config result, 'Aa Bb and Bb Cc', 'ab bc', 'the-pair+aa+bb@the-host.com'
158
+ end
159
+
160
+ it 'can set n users as pair' do
161
+ result = run 'git pair ab bc cd'
162
+ expect_config result, 'Aa Bb, Bb Cc and Cc Dd', 'ab bc cd', 'the-pair+aa+bb+cc@the-host.com'
163
+ end
164
+
165
+ it 'prints names, email addresses, and initials in alphabetical order' do
166
+ result = run 'git pair ab cd bc'
167
+ expect_config result, 'Aa Bb, Bb Cc and Cc Dd', 'ab bc cd', 'the-pair+aa+bb+cc@the-host.com'
168
+ end
169
+
170
+ it 'can set a user with apostrophes as pair' do
171
+ write '.pairs', File.read('.pairs').sub('Aa Bb', "Pete O'Connor")
172
+ result = run 'git pair ab'
173
+ expect_config result, "Pete O'Connor", 'ab', 'the-pair+pete@the-host.com'
174
+ end
175
+
176
+ it 'fails when there is no .git in the tree' do
177
+ run 'rm -f /tmp/pairs'
178
+ run 'cp .pairs /tmp'
179
+ Dir.chdir '/tmp' do
180
+ result = run 'git pair ab 2>&1', fail: true
181
+ result.should include('Not a git repository (or any of the parent directories)')
182
+ end
183
+ run 'rm -f /tmp/pairs'
184
+ end
185
+
186
+ it 'finds .pairs file in lower parent folder' do
187
+ run 'mkdir foo'
188
+ Dir.chdir 'foo' do
189
+ result = run 'git pair ab'
190
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com'
191
+ end
192
+ end
193
+
194
+ it 'unsets local config when no argument is passed' do
195
+ run 'git pair ab --global'
196
+ run 'git pair bc'
197
+ result = run 'git pair'
198
+ result.should include 'Unset user.name, user.email, user.initials'
199
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com', global: true
200
+ result.should_not include('local:')
201
+ end
202
+
203
+ it 'uses hard email when given' do
204
+ write '.pairs', File.read('.pairs').sub(/email:.*/m, 'email: foo@bar.com')
205
+ result = run 'git pair ab'
206
+ expect_config result, 'Aa Bb', 'ab', 'foo@bar.com'
207
+ end
208
+
209
+ context 'when no email config is present' do
210
+ before do
211
+ write '.pairs', File.read('.pairs').sub(/email:.*/m, '')
212
+ end
213
+
214
+ it "doesn't set email" do
215
+ run 'git pair ab'
216
+ git_config_value('email').should be_empty
217
+ end
218
+
219
+ it "doesn't report about email" do
220
+ result = run 'git pair ab'
221
+ result.should_not include 'email'
222
+ end
223
+ end
224
+
225
+ it 'uses no email prefix when only host is given' do
226
+ write '.pairs', File.read('.pairs').sub(/email:.*/m, "email:\n domain: foo.com")
227
+ result = run 'git pair ab'
228
+ expect_config result, 'Aa Bb', 'ab', 'aa@foo.com'
229
+ end
230
+
231
+ context 'when no no_solo_prefix is given' do
232
+ before do
233
+ write '.pairs',
234
+ File.read('.pairs').sub(/email:.*/m,
235
+ "email:\n prefix: pairs\n no_solo_prefix: true\n domain: foo.com")
236
+ end
237
+
238
+ it 'uses no email prefix for single developers' do
239
+ result = run 'git pair ab'
240
+ expect_config result, 'Aa Bb', 'ab', 'aa@foo.com'
241
+ end
242
+
243
+ it 'uses email prefix for multiple developers' do
244
+ result = run 'git pair ab bc'
245
+ expect_config result, 'Aa Bb and Bb Cc', 'ab bc', 'pairs+aa+bb@foo.com'
246
+ end
247
+ end
248
+
249
+ it 'fails with unknown initials' do
250
+ result = run 'git pair xx', fail: true
251
+ result.should include("Couldn't find author name for initials: xx")
252
+ end
253
+
254
+ it 'uses alternate email prefix' do
255
+ write '.pairs', File.read('.pairs').sub(/ab:.*/, 'ab: Aa Bb; blob')
256
+ result = run 'git pair ab'
257
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+blob@the-host.com'
258
+ end
259
+ end
260
+
261
+ context 'without a .pairs file in the tree' do
262
+ around do |example|
263
+ Dir.chdir '/tmp' do
264
+ run 'rm -f .pairs'
265
+ dir = 'git_stats_test'
266
+ run "rm -rf #{dir}"
267
+ run "mkdir #{dir}"
268
+ Dir.chdir dir do
269
+ run 'git init'
270
+ example.run
271
+ end
272
+ run "rm -rf #{dir}"
273
+ end
274
+ end
275
+
276
+ context 'and without a .pairs file in the home directory' do
277
+ it 'fails if it cannot find a pairs file' do
278
+ run 'git pair ab', fail: true
279
+ end
280
+
281
+ it 'prints instructions' do
282
+ result = run 'git pair ab', fail: true
283
+ result.should include('Could not find a .pairs file. Create a YAML file in your project or home directory.')
284
+ end
285
+ end
286
+
287
+ context 'but a .pairs file in the home directory' do
288
+ around do |example|
289
+ file = File.join(ENV['HOME'], '.pairs')
290
+ write file, <<-YAML.unindent
291
+ pairs:
292
+ ab: Aa Bb
293
+ bc: Bb Cc
294
+ cd: Cc Dd
295
+
296
+ email:
297
+ prefix: the-pair
298
+ domain: the-host.com
299
+ YAML
300
+
301
+ example.run
302
+
303
+ FileUtils.rm file
304
+ end
305
+
306
+ it 'loads the file' do
307
+ result = run 'git pair ab'
308
+ expect_config result, 'Aa Bb', 'ab', 'the-pair+aa@the-host.com'
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ describe 'pair-commit' do
315
+ before do
316
+ write '.pairs', <<-YAML.unindent
317
+ pairs:
318
+ ab: Aa Bb; abb
319
+ bc: Bb Cc; bcc
320
+ cd: Cc Dd; cdd
321
+
322
+ email:
323
+ prefix: the-pair
324
+ domain: the-host.com
325
+
326
+ email_addresses:
327
+ bc: test@other-host.com
328
+ YAML
329
+ end
330
+
331
+ context 'when a pair has been set' do
332
+ before do
333
+ run 'git pair ab cd'
334
+ end
335
+
336
+ def author_name_of_last_commit
337
+ (run 'git log -1 --pretty=%an').strip
338
+ end
339
+
340
+ def author_email_of_last_commit
341
+ (run 'git log -1 --pretty=%ae').strip
342
+ end
343
+
344
+ def committer_name_of_last_commit
345
+ (run 'git log -1 --pretty=%cn').strip
346
+ end
347
+
348
+ def committer_email_of_last_commit
349
+ (run 'git log -1 --pretty=%ce').strip
350
+ end
351
+
352
+ it 'makes a commit' do
353
+ git_pair_commit
354
+ output = run 'git log -1'
355
+ output.should include('Pair pare pear')
356
+ end
357
+
358
+ it "sets the author name to the pair's names" do
359
+ git_pair_commit
360
+ output = run 'git log -1 --pretty=%an'
361
+ output.strip.should eq('Aa Bb and Cc Dd')
362
+ end
363
+
364
+ it 'randomly chooses from pair and sets user.email' do
365
+ emails = 6.times.map do
366
+ git_pair_commit
367
+ author_email_of_last_commit
368
+ end.uniq
369
+ emails.should =~ ['abb@the-host.com', 'cdd@the-host.com']
370
+ end
371
+
372
+ context 'when git options are passed' do
373
+ it 'forwards those options to git' do
374
+ git_pair_commit
375
+ run 'git pair ab bc'
376
+ run 'git pair-commit --amend -C HEAD --reset-author'
377
+
378
+ output = run 'git log -1 --pretty=%an'
379
+ output.strip.should eq('Aa Bb and Bb Cc')
380
+ end
381
+ end
382
+
383
+ context 'when the pair is set globally and the local repo has custom user name and email' do
384
+ before do
385
+ run 'git pair --global ab cd'
386
+ run "git config user.name 'Betty White'"
387
+ run "git config user.email 'betty@example.com'"
388
+ end
389
+
390
+ it 'still makes the commit with the correct user name' do
391
+ git_pair_commit
392
+
393
+ author_name_of_last_commit.should eq('Aa Bb and Cc Dd')
394
+ end
395
+
396
+ it 'still makes the commit with the correct user email' do
397
+ git_pair_commit
398
+
399
+ %w[abb@the-host.com cdd@the-host.com].should include(author_email_of_last_commit)
400
+ end
401
+
402
+ it 'still makes the commit with the correct committer name' do
403
+ git_pair_commit
404
+
405
+ committer_name_of_last_commit.should eq('Aa Bb and Cc Dd')
406
+ end
407
+
408
+ it 'still makes the commit with the correct committer email' do
409
+ git_pair_commit
410
+
411
+ %w[abb@the-host.com cdd@the-host.com].should include(committer_email_of_last_commit)
412
+ end
413
+ end
414
+
415
+ context 'when one of the pair has a custom email address' do
416
+ before do
417
+ run 'git pair ab bc'
418
+ end
419
+
420
+ it 'uses that email address' do
421
+ emails = 6.times.map do
422
+ git_pair_commit
423
+ author_email_of_last_commit
424
+ end.uniq
425
+ emails.should =~ ['abb@the-host.com', 'test@other-host.com']
426
+ end
427
+ end
428
+ end
429
+
430
+ context 'when no pair has been set' do
431
+ it 'raises an exception' do
432
+ git_pair_commit.should include('Error: No pair set')
433
+ end
434
+ end
435
+
436
+ context 'when -h flag is passed' do
437
+ it 'shows the help message' do
438
+ results = run 'git pair-commit -h'
439
+ results.gsub(/\s+/, ' ').should include('randomly chooses the author email from the members of the pair')
440
+ end
441
+ end
442
+
443
+ def git_pair_commit
444
+ run "echo #{rand(100)} > b"
445
+ run 'git add b'
446
+ run 'git pair-commit -m "Pair pare pear"', fail: true
447
+ end
448
+ end
449
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pivotal_git_scripts/git_pair'
4
+
5
+ describe PivotalGitScripts::GitPair::Runner do
6
+ let(:runner) { described_class.new }
7
+
8
+ describe 'set_git_config' do
9
+ it 'calls git config with pairs in the options' do
10
+ runner.should_receive(:system).with('git config user.foo "bar baz"')
11
+
12
+ runner.set_git_config(false, 'foo' => 'bar baz')
13
+ end
14
+
15
+ it 'can unset git config options' do
16
+ runner.should_receive(:system).with('git config --unset user.foo')
17
+
18
+ runner.set_git_config(false, 'foo' => nil)
19
+ end
20
+
21
+ it 'can handle multiple pairs in a hash' do
22
+ runner.should_receive(:system).with('git config --unset user.remove')
23
+ runner.should_receive(:system).with('git config user.ten "10"')
24
+
25
+ runner.set_git_config(false, 'remove' => nil, 'ten' => '10')
26
+ end
27
+
28
+ it 'supports a global option' do
29
+ runner.should_receive(:system).with('git config --global user.foo "bar baz"')
30
+
31
+ runner.set_git_config(true, 'foo' => 'bar baz')
32
+ end
33
+ end
34
+
35
+ describe 'read_author_info_from_config' do
36
+ it 'maps from the initials to the full name' do
37
+ config = {
38
+ 'pairs' => {
39
+ 'aa' => 'An Aardvark',
40
+ 'tt' => 'The Turtle'
41
+ }
42
+ }
43
+
44
+ names = runner.read_author_info_from_config(config, %w[aa tt])
45
+ names.should =~ ['An Aardvark', 'The Turtle']
46
+ end
47
+
48
+ it 'exits when initials cannot be found' do
49
+ expect do
50
+ runner.read_author_info_from_config({ 'pairs' => {} }, ['aa'])
51
+ end.to raise_error(PivotalGitScripts::GitPair::GitPairException)
52
+ end
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pivotal_git_scripts_3
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Pivotal Labs
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: These scripts are helpers for managing developer workflow when using
13
+ git repos hosted on GitHub.
14
+ email:
15
+ - gems@pivotallabs.com
16
+ executables:
17
+ - git-about
18
+ - git-pair
19
+ - git-pair-commit
20
+ - git-project
21
+ - git-superpull
22
+ extensions: []
23
+ extra_rdoc_files: []
24
+ files:
25
+ - CHANGELOG.md
26
+ - MIT.LICENSE
27
+ - README.md
28
+ - bin/git-about
29
+ - bin/git-pair
30
+ - bin/git-pair-commit
31
+ - bin/git-project
32
+ - bin/git-superpull
33
+ - lib/pivotal_git_scripts/git_pair.rb
34
+ - lib/pivotal_git_scripts/version.rb
35
+ - spec/cli_spec.rb
36
+ - spec/git_pair_spec.rb
37
+ homepage: https://github.com/akkadaya/pivotal_git_scripts_3
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '3.0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.7.1
56
+ specification_version: 4
57
+ summary: Developer git workflow convenience scripts
58
+ test_files:
59
+ - spec/cli_spec.rb
60
+ - spec/git_pair_spec.rb