pivotal_git_scripts 1.1.4 → 1.4.0
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/.travis.yml +15 -0
- data/CHANGELOG.md +26 -0
- data/Gemfile +1 -1
- data/README.md +23 -0
- data/bin/git-pair +3 -148
- data/bin/git-pair-commit +5 -0
- data/lib/pivotal_git_scripts/git_pair.rb +251 -0
- data/lib/pivotal_git_scripts/version.rb +1 -1
- data/spec/cli_spec.rb +213 -13
- data/spec/git_pair_spec.rb +52 -0
- metadata +17 -14
- data/Gemfile.lock +0 -29
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 753d8a062ae624181090e916384a9e8e701297dd
|
4
|
+
data.tar.gz: a650f7c1b75326dd6f220d6c3b0608e1e897a84f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 305b19b837375798d916f98aec979e51d38a547cf44aa41333d3543402ac5e3a701f0362ac667fc6de1d50a4e6dc7e3687b6fc9246870edd30d06a4881814abd
|
7
|
+
data.tar.gz: eb1992aa9831cf19e157306129374a8b8bc5262584fb7f3d5c6a957ea39adc0c51a75884bcf31a661d19e9f17a12267ac5a836bb37e8c36fc7390f3dc73a66fa
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.1.1
|
4
|
+
- 2.0.0
|
5
|
+
- 1.9.3
|
6
|
+
- 1.9.2
|
7
|
+
# Fix weird issues from this Travis build:
|
8
|
+
# https://travis-ci.org/pivotal/git_scripts/jobs/5501358
|
9
|
+
# https://git.wiki.kernel.org/index.php/Git_FAQ#Git_commit_is_dying_telling_me_.22fatal:_empty_ident_.3Cuser.40myhost.3E_not_allowed.22.2C_what.27s_wrong.3F
|
10
|
+
env:
|
11
|
+
global:
|
12
|
+
- GIT_AUTHOR_NAME="Travis CI"
|
13
|
+
before_script:
|
14
|
+
- git config --global user.email "travis-ci@example.com"
|
15
|
+
- git config --global user.name "Travis CI"
|
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/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/pivotal/git_scripts.png)](https://travis-ci.org/pivotal/git_scripts)
|
2
|
+
|
1
3
|
# Git Scripts
|
2
4
|
|
3
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`.
|
@@ -46,6 +48,27 @@ Options are:
|
|
46
48
|
-v, --version Show Version
|
47
49
|
-h, --help Show this.
|
48
50
|
|
51
|
+
## git-pair-commit
|
52
|
+
|
53
|
+
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.
|
54
|
+
|
55
|
+
### Using git-pair-commit in RubyMine
|
56
|
+
RubyMine already supports pointing at a custom location for your git executable in the Preferences -> Version Control -> Git
|
57
|
+
![screen shot 2014-03-11 at 12 49 02 pm](https://f.cloud.github.com/assets/163532/2390097/49c3023e-a956-11e3-8aeb-dcba1a814309.png)
|
58
|
+
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:
|
59
|
+
```ruby
|
60
|
+
#!/usr/bin/env ruby
|
61
|
+
|
62
|
+
exit_code = if ARGV[1] == "commit"
|
63
|
+
system "git pair-commit #{ARGV[1..-1].join(" ")}"
|
64
|
+
else
|
65
|
+
system "git #{ARGV.join(" ")}"
|
66
|
+
end
|
67
|
+
|
68
|
+
exit exit_code
|
69
|
+
```
|
70
|
+
Make sure it's executable.
|
71
|
+
|
49
72
|
## git-project
|
50
73
|
|
51
74
|
$ git project pivots
|
data/bin/git-pair
CHANGED
@@ -1,150 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require '
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__)) # this is required when running from /usr/local/bin
|
3
|
+
require 'pivotal_git_scripts/git_pair'
|
4
4
|
|
5
|
-
|
6
|
-
options = {}
|
7
|
-
OptionParser.new do |opts|
|
8
|
-
# copy-paste from readme
|
9
|
-
opts.banner = <<BANNER.sub('<br/>','')
|
10
|
-
Configures git authors when pair programming.
|
11
|
-
|
12
|
-
git pair sp js
|
13
|
-
user.name=Josh Susser & Sam Pierson
|
14
|
-
user.email=pair+jsusser+sam@pivotallabs.com
|
15
|
-
|
16
|
-
|
17
|
-
Create a `.pairs` config file in project root or your home folder.
|
18
|
-
|
19
|
-
# .pairs - configuration for 'git pair'
|
20
|
-
pairs:
|
21
|
-
# <initials>: <Firstname> <Lastname>[; <email-id>]
|
22
|
-
eh: Edward Hieatt
|
23
|
-
js: Josh Susser; jsusser
|
24
|
-
sf: Serguei Filimonov; serguei
|
25
|
-
email:
|
26
|
-
prefix: pair
|
27
|
-
domain: pivotallabs.com
|
28
|
-
# no_solo_prefix: true
|
29
|
-
#global: true
|
30
|
-
|
31
|
-
|
32
|
-
By default this affects the current project (.git/config).<br/>
|
33
|
-
Use the `--global` option or add `global: true` to your `.pairs` file to set the global git configuration for all projects (~/.gitconfig).
|
34
|
-
|
35
|
-
Options are:
|
36
|
-
BANNER
|
37
|
-
opts.on("-g", "--global", "Modify global git options instead of local") { options[:global] = true }
|
38
|
-
opts.on("-v", "--version", "Show Version") do
|
39
|
-
$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
|
40
|
-
require "pivotal_git_scripts/version"
|
41
|
-
puts PivotalGitScripts::VERSION
|
42
|
-
exit
|
43
|
-
end
|
44
|
-
opts.on("-h", "--help", "Show this.") { puts opts; exit }
|
45
|
-
end.parse!(argv)
|
46
|
-
|
47
|
-
options
|
48
|
-
end
|
49
|
-
|
50
|
-
def read_pairs_config
|
51
|
-
pairs_file_path = nil
|
52
|
-
candidate_file_path = '.pairs'
|
53
|
-
until pairs_file_path || File.expand_path(candidate_file_path) == '/.pairs' do
|
54
|
-
if File.exists?(candidate_file_path)
|
55
|
-
pairs_file_path = candidate_file_path
|
56
|
-
else
|
57
|
-
candidate_file_path = File.join("..", candidate_file_path)
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
unless pairs_file_path
|
62
|
-
puts <<-INSTRUCTIONS
|
63
|
-
Could not find a .pairs file. Create a YAML file in your project or home directory.
|
64
|
-
Format: <initials>: <name>[; <email>]
|
65
|
-
Example:
|
66
|
-
# .pairs - configuration for 'git pair'
|
67
|
-
# place in project or home directory
|
68
|
-
pairs:
|
69
|
-
eh: Edward Hieatt
|
70
|
-
js: Josh Susser; jsusser
|
71
|
-
sf: Serguei Filimonov; serguei
|
72
|
-
email:
|
73
|
-
prefix: pair
|
74
|
-
domain: pivotallabs.com
|
75
|
-
INSTRUCTIONS
|
76
|
-
exit(1)
|
77
|
-
end
|
78
|
-
pairs_file_path ? YAML.load_file(pairs_file_path) : {}
|
79
|
-
end
|
80
|
-
|
81
|
-
def read_author_info_from_config(config, initials)
|
82
|
-
initials.map do |initials|
|
83
|
-
if full_name = config['pairs'][initials.downcase]
|
84
|
-
full_name
|
85
|
-
else
|
86
|
-
puts "Couldn't find author name for initials: #{initials}. Add this person to the .pairs file in your project or home directory."
|
87
|
-
exit 1
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def build_email(emails, config)
|
93
|
-
if config.is_a?(Hash)
|
94
|
-
prefix = config['prefix'] if !config['no_solo_prefix'] or emails.size > 1
|
95
|
-
"#{([prefix] + emails).compact.join('+')}@#{config['domain']}"
|
96
|
-
else
|
97
|
-
config
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def set_git_config(global_config_string, options)
|
102
|
-
options.each do |key,value|
|
103
|
-
key = "user.#{key}"
|
104
|
-
value = value ? %Q{#{key} "#{value}"} : "--unset #{key}"
|
105
|
-
system(%Q{git config#{global_config_string} #{value}})
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def report_git_settings(git_dir, key)
|
110
|
-
global = `git config --global --get-regexp '^user\.#{key}'`
|
111
|
-
local = `git config -f #{git_dir}/config --get-regexp '^user\.#{key}'`
|
112
|
-
if global.length > 0 && local.length > 0
|
113
|
-
puts "NOTE: Overriding global user.#{key} setting with local."
|
114
|
-
end
|
115
|
-
puts "global: #{global}" if global.length > 0
|
116
|
-
puts "local: #{local}" if local.length > 0
|
117
|
-
end
|
118
|
-
|
119
|
-
def extract_author_names_and_email_ids_from_config(config, initials)
|
120
|
-
authors = read_author_info_from_config(config, initials)
|
121
|
-
authors.sort!.uniq!
|
122
|
-
authors.map do |a|
|
123
|
-
full_name, email_id = a.split(";").map(&:strip)
|
124
|
-
email_id ||= full_name.split(' ').first.downcase
|
125
|
-
[full_name, email_id]
|
126
|
-
end.transpose
|
127
|
-
end
|
128
|
-
|
129
|
-
git_dir = `git rev-parse --git-dir`.chomp
|
130
|
-
exit 1 if git_dir.empty?
|
131
|
-
|
132
|
-
options = parse_cli_options(ARGV)
|
133
|
-
initials = ARGV
|
134
|
-
config = read_pairs_config
|
135
|
-
global = " --global" if options[:global] or config["global"]
|
136
|
-
|
137
|
-
if initials.any?
|
138
|
-
author_names, email_ids = extract_author_names_and_email_ids_from_config(config, initials)
|
139
|
-
authors = [author_names[0..-2].join(", "), author_names.last].reject(&:empty?).join(" & ")
|
140
|
-
email = build_email(email_ids, config["email"])
|
141
|
-
|
142
|
-
set_git_config global, :name => authors, :email => email, :initials => initials.join(" ")
|
143
|
-
else
|
144
|
-
set_git_config global, :name => nil, :email => nil, :initials => nil
|
145
|
-
puts "Unset#{global} user.name, user.email, user.initials"
|
146
|
-
end
|
147
|
-
|
148
|
-
[:name, :email, :initials].each do |key|
|
149
|
-
report_git_settings(git_dir, key)
|
150
|
-
end
|
5
|
+
PivotalGitScripts::GitPair.main(ARGV)
|
data/bin/git-pair-commit
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
require "pivotal_git_scripts/version"
|
2
|
+
require 'yaml'
|
3
|
+
require 'optparse'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
module PivotalGitScripts
|
7
|
+
module GitPair
|
8
|
+
def self.main(argv)
|
9
|
+
runner = Runner.new
|
10
|
+
runner.main(argv)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.commit(argv)
|
14
|
+
runner = Runner.new
|
15
|
+
runner.commit(argv)
|
16
|
+
end
|
17
|
+
|
18
|
+
class GitPairException < Exception; end
|
19
|
+
|
20
|
+
class Runner
|
21
|
+
def main(argv)
|
22
|
+
git_dir = `git rev-parse --git-dir`.chomp
|
23
|
+
exit 1 if git_dir.empty?
|
24
|
+
|
25
|
+
options = parse_cli_options(argv)
|
26
|
+
initials = argv
|
27
|
+
config = read_pairs_config
|
28
|
+
global = !!(options[:global] || config["global"])
|
29
|
+
|
30
|
+
if initials.any?
|
31
|
+
author_names, email_ids = extract_author_names_and_email_ids_from_config(config, initials)
|
32
|
+
authors = pair_names(author_names)
|
33
|
+
git_config = {:name => authors, :initials => initials.sort.join(" ")}
|
34
|
+
git_config[:email] = build_email(email_ids, config["email"]) unless no_email(config)
|
35
|
+
set_git_config global, git_config
|
36
|
+
else
|
37
|
+
git_config = {:name => nil, :initials => nil}
|
38
|
+
git_config[:email] = nil unless no_email(config)
|
39
|
+
set_git_config global, git_config
|
40
|
+
puts "Unset#{' global' if global} user.name, #{'user.email, ' unless no_email(config)}user.initials"
|
41
|
+
end
|
42
|
+
|
43
|
+
[:name, :email, :initials].each do |key|
|
44
|
+
report_git_settings(git_dir, key)
|
45
|
+
end
|
46
|
+
rescue GitPairException => e
|
47
|
+
puts e.message
|
48
|
+
exit 1
|
49
|
+
end
|
50
|
+
|
51
|
+
def commit(argv)
|
52
|
+
if argv[0] == '-h'
|
53
|
+
puts 'Usage: git pair-commit [options_for_git_commit]'
|
54
|
+
puts ''
|
55
|
+
puts 'Commits changes to the repository using `git commit`, but randomly chooses the author email from the'
|
56
|
+
puts 'members of the pair. In order for GitHub to assign credit for the commit activity, the user\'s email'
|
57
|
+
puts 'must be linked in their GitHub account.'
|
58
|
+
exit 0
|
59
|
+
end
|
60
|
+
|
61
|
+
config = read_pairs_config
|
62
|
+
author_details = extract_author_details_from_config(config, current_pair_initials)
|
63
|
+
author_names = author_details.keys.map { |i| author_details[i][:name] }
|
64
|
+
authors = pair_names(author_names)
|
65
|
+
author_email = random_author_email(author_details)
|
66
|
+
puts "Committing under #{author_email}"
|
67
|
+
passthrough_args = argv.map{|arg| "'#{arg}'"}.join(' ')
|
68
|
+
env_variables = "GIT_AUTHOR_NAME='#{authors}' GIT_AUTHOR_EMAIL='#{author_email}' GIT_COMMITTER_NAME='#{authors}' GIT_COMMITTER_EMAIL='#{author_email}'"
|
69
|
+
system "#{env_variables} git commit #{passthrough_args}"
|
70
|
+
rescue GitPairException => e
|
71
|
+
puts e.message
|
72
|
+
exit 1
|
73
|
+
end
|
74
|
+
|
75
|
+
def current_pair_initials
|
76
|
+
initials = `git config user.initials`.strip.split(' ')
|
77
|
+
raise GitPairException, 'Error: No pair set. Please set your pair with `git pair ...`' if initials.empty?
|
78
|
+
initials
|
79
|
+
end
|
80
|
+
|
81
|
+
def parse_cli_options(argv)
|
82
|
+
options = {}
|
83
|
+
OptionParser.new do |opts|
|
84
|
+
# copy-paste from readme
|
85
|
+
opts.banner = <<BANNER.sub('<br/>','')
|
86
|
+
Configures git authors when pair programming.
|
87
|
+
|
88
|
+
git pair sp js
|
89
|
+
user.name=Josh Susser and Sam Pierson
|
90
|
+
user.email=pair+jsusser+sam@pivotallabs.com
|
91
|
+
|
92
|
+
|
93
|
+
Create a `.pairs` config file in project root or your home folder.
|
94
|
+
|
95
|
+
# .pairs - configuration for 'git pair'
|
96
|
+
pairs:
|
97
|
+
# <initials>: <Firstname> <Lastname>[; <email-id>]
|
98
|
+
eh: Edward Hieatt
|
99
|
+
js: Josh Susser; jsusser
|
100
|
+
sf: Serguei Filimonov; serguei
|
101
|
+
# if email section is present, email will be set
|
102
|
+
# if you leave out the email config section, email will not be set
|
103
|
+
email:
|
104
|
+
prefix: pair
|
105
|
+
domain: pivotallabs.com
|
106
|
+
# no_solo_prefix: true
|
107
|
+
#global: true
|
108
|
+
# include the following section to set custom email addresses for users
|
109
|
+
#email_addresses:
|
110
|
+
# zr: zach.robinson@example.com
|
111
|
+
|
112
|
+
|
113
|
+
By default this affects the current project (.git/config).<br/>
|
114
|
+
Use the `--global` option or add `global: true` to your `.pairs` file to set the global git configuration for all projects (~/.gitconfig).
|
115
|
+
|
116
|
+
Options are:
|
117
|
+
BANNER
|
118
|
+
opts.on("-g", "--global", "Modify global git options instead of local") { options[:global] = true }
|
119
|
+
opts.on("-v", "--version", "Show Version") do
|
120
|
+
puts PivotalGitScripts::VERSION
|
121
|
+
exit
|
122
|
+
end
|
123
|
+
opts.on("-h", "--help", "Show this.") { puts opts; exit }
|
124
|
+
end.parse!(argv)
|
125
|
+
|
126
|
+
options
|
127
|
+
end
|
128
|
+
|
129
|
+
def read_pairs_config
|
130
|
+
pairs_file_name = '.pairs'
|
131
|
+
|
132
|
+
directory = File.absolute_path(Dir.pwd)
|
133
|
+
candidate_directories = [directory]
|
134
|
+
while ! Pathname.new(directory).root? do
|
135
|
+
directory = File.absolute_path(File.join(directory, ".."))
|
136
|
+
candidate_directories << directory
|
137
|
+
end
|
138
|
+
home = File.absolute_path(ENV["HOME"])
|
139
|
+
candidate_directories << home unless candidate_directories.include? home
|
140
|
+
|
141
|
+
pairs_file_path = candidate_directories.
|
142
|
+
map { |d| File.join(d, ".pairs") }.
|
143
|
+
find { |f| File.exists? f }
|
144
|
+
|
145
|
+
unless pairs_file_path
|
146
|
+
raise GitPairException, <<-INSTRUCTIONS
|
147
|
+
Could not find a .pairs file. Create a YAML file in your project or home directory.
|
148
|
+
Format: <initials>: <name>[; <email>]
|
149
|
+
Example:
|
150
|
+
# .pairs - configuration for 'git pair'
|
151
|
+
# place in project or home directory
|
152
|
+
pairs:
|
153
|
+
eh: Edward Hieatt
|
154
|
+
js: Josh Susser; jsusser
|
155
|
+
sf: Serguei Filimonov; serguei
|
156
|
+
email:
|
157
|
+
prefix: pair
|
158
|
+
domain: pivotallabs.com
|
159
|
+
INSTRUCTIONS
|
160
|
+
end
|
161
|
+
|
162
|
+
YAML.load_file(pairs_file_path)
|
163
|
+
end
|
164
|
+
|
165
|
+
def read_author_info_from_config(config, initials_ary)
|
166
|
+
initials_ary.map do |initials|
|
167
|
+
config['pairs'][initials.downcase] or
|
168
|
+
raise GitPairException, "Couldn't find author name for initials: #{initials}. Add this person to the .pairs file in your project or home directory."
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def build_email(emails, config)
|
173
|
+
if config.is_a?(Hash)
|
174
|
+
prefix = config['prefix'] if !config['no_solo_prefix'] or emails.size > 1
|
175
|
+
"#{([prefix] + emails).compact.join('+')}@#{config['domain']}"
|
176
|
+
else
|
177
|
+
config
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def random_author_email(author_details)
|
182
|
+
author_id = author_details.keys.sample
|
183
|
+
author_details[author_id][:email]
|
184
|
+
end
|
185
|
+
|
186
|
+
def set_git_config(global, options)
|
187
|
+
options.each do |key,value|
|
188
|
+
config_key = "user.#{key}"
|
189
|
+
arg = value ? %Q{#{config_key} "#{value}"} : "--unset #{config_key}"
|
190
|
+
system(%Q{git config#{' --global' if global} #{arg}})
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def report_git_settings(git_dir, key)
|
195
|
+
global = `git config --global --get-regexp '^user\.#{key}'`
|
196
|
+
local = `git config -f #{git_dir}/config --get-regexp '^user\.#{key}'`
|
197
|
+
if global.length > 0 && local.length > 0
|
198
|
+
puts "NOTE: Overriding global user.#{key} setting with local."
|
199
|
+
end
|
200
|
+
puts "global: #{global}" if global.length > 0
|
201
|
+
puts "local: #{local}" if local.length > 0
|
202
|
+
end
|
203
|
+
|
204
|
+
def extract_author_names_and_email_ids_from_config(config, initials)
|
205
|
+
authors = read_author_info_from_config(config, initials)
|
206
|
+
authors.sort!.uniq! # FIXME
|
207
|
+
authors.map do |a|
|
208
|
+
full_name, email_id = a.split(";").map(&:strip)
|
209
|
+
email_id ||= full_name.split(' ').first.downcase
|
210
|
+
[full_name, email_id]
|
211
|
+
end.transpose
|
212
|
+
end
|
213
|
+
|
214
|
+
def no_email(config)
|
215
|
+
!config.key? 'email'
|
216
|
+
end
|
217
|
+
|
218
|
+
def extract_author_details_from_config(config, initials)
|
219
|
+
details = {}
|
220
|
+
|
221
|
+
initials.each do |i|
|
222
|
+
info = read_author_info_from_config(config, [i]).first
|
223
|
+
|
224
|
+
full_name, email_id = info.split(";").map(&:strip)
|
225
|
+
email_id ||= full_name.split(' ').first.downcase
|
226
|
+
|
227
|
+
email = read_custom_email_address_from_config(config, i)
|
228
|
+
email ||= "#{email_id}@#{config['email']['domain']}"
|
229
|
+
|
230
|
+
details[i] = {
|
231
|
+
:name => full_name,
|
232
|
+
:email => email
|
233
|
+
}
|
234
|
+
end
|
235
|
+
|
236
|
+
details
|
237
|
+
end
|
238
|
+
|
239
|
+
def read_custom_email_address_from_config(config, initial)
|
240
|
+
return nil unless config['email_addresses']
|
241
|
+
return config['email_addresses'][initial.downcase]
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
def pair_names(author_names)
|
247
|
+
[author_names[0..-2].join(", "), author_names.last].reject(&:empty?).join(" and ")
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
data/spec/cli_spec.rb
CHANGED
@@ -7,9 +7,13 @@ describe "CLI" do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def run(command, options={})
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
output = `#{command}`
|
11
|
+
return output if $?.success?
|
12
|
+
return output if options[:fail]
|
13
|
+
|
14
|
+
message = "Unable to run #{command.inspect} in #{Dir.pwd}.\n#{output}"
|
15
|
+
warn "ERROR: #{message}"
|
16
|
+
raise message
|
13
17
|
end
|
14
18
|
|
15
19
|
def write(file, content)
|
@@ -23,10 +27,17 @@ describe "CLI" do
|
|
23
27
|
|
24
28
|
# use fake home for .ssh hacks
|
25
29
|
run "mkdir #{dir}/home"
|
26
|
-
ENV["HOME"] = File.
|
30
|
+
ENV["HOME"] = File.absolute_path("#{dir}/home")
|
27
31
|
|
28
32
|
Dir.chdir dir do
|
29
|
-
run "touch a
|
33
|
+
run "touch a"
|
34
|
+
run "git init"
|
35
|
+
run "git add ."
|
36
|
+
run "git config user.email 'rspec-tests@example.com'"
|
37
|
+
run "git config user.name 'rspec test suite'"
|
38
|
+
run "git commit -am 'initial'"
|
39
|
+
run "git config --unset user.email"
|
40
|
+
run "git config --unset user.name"
|
30
41
|
example.run
|
31
42
|
end
|
32
43
|
end
|
@@ -82,6 +93,11 @@ describe "CLI" do
|
|
82
93
|
result.should include "#{prefix}user.email #{email}"
|
83
94
|
end
|
84
95
|
|
96
|
+
def git_config_value(name, global = false)
|
97
|
+
global_prefix = "cd /tmp && " if global
|
98
|
+
`#{global_prefix}git config user.#{name}`
|
99
|
+
end
|
100
|
+
|
85
101
|
it "prints help" do
|
86
102
|
result = run "git-pair --help"
|
87
103
|
result.should include("Configures git authors when pair programming")
|
@@ -136,12 +152,17 @@ describe "CLI" do
|
|
136
152
|
|
137
153
|
it "can set a 2 users as pair" do
|
138
154
|
result = run "git pair ab bc"
|
139
|
-
expect_config result, "Aa Bb
|
155
|
+
expect_config result, "Aa Bb and Bb Cc", "ab bc", "the-pair+aa+bb@the-host.com"
|
140
156
|
end
|
141
157
|
|
142
158
|
it "can set n users as pair" do
|
143
159
|
result = run "git pair ab bc cd"
|
144
|
-
expect_config result, "Aa Bb, Bb Cc
|
160
|
+
expect_config result, "Aa Bb, Bb Cc and Cc Dd", "ab bc cd", "the-pair+aa+bb+cc@the-host.com"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "prints names, email addresses, and initials in alphabetical order" do
|
164
|
+
result = run "git pair ab cd bc"
|
165
|
+
expect_config result, "Aa Bb, Bb Cc and Cc Dd", "ab bc cd", "the-pair+aa+bb+cc@the-host.com"
|
145
166
|
end
|
146
167
|
|
147
168
|
it "can set a user with apostrophes as pair" do
|
@@ -183,6 +204,22 @@ describe "CLI" do
|
|
183
204
|
expect_config result, "Aa Bb", "ab", "foo@bar.com"
|
184
205
|
end
|
185
206
|
|
207
|
+
context "when no email config is present" do
|
208
|
+
before do
|
209
|
+
write ".pairs", File.read(".pairs").sub(/email:.*/m, "")
|
210
|
+
end
|
211
|
+
|
212
|
+
it "doesn't set email" do
|
213
|
+
run "git pair ab"
|
214
|
+
git_config_value('email').should be_empty
|
215
|
+
end
|
216
|
+
|
217
|
+
it "doesn't report about email" do
|
218
|
+
result = run "git pair ab"
|
219
|
+
result.should_not include "email"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
186
223
|
it "uses no email prefix when only host is given" do
|
187
224
|
write ".pairs", File.read(".pairs").sub(/email:.*/m, "email:\n domain: foo.com")
|
188
225
|
result = run "git pair ab"
|
@@ -201,7 +238,7 @@ describe "CLI" do
|
|
201
238
|
|
202
239
|
it "uses email prefix for multiple developers" do
|
203
240
|
result = run "git pair ab bc"
|
204
|
-
expect_config result, "Aa Bb
|
241
|
+
expect_config result, "Aa Bb and Bb Cc", "ab bc", "pairs+aa+bb@foo.com"
|
205
242
|
end
|
206
243
|
end
|
207
244
|
|
@@ -232,14 +269,177 @@ describe "CLI" do
|
|
232
269
|
end
|
233
270
|
end
|
234
271
|
|
235
|
-
|
236
|
-
|
272
|
+
context "and without a .pairs file in the home directory" do
|
273
|
+
it "fails if it cannot find a pairs file" do
|
274
|
+
run "git pair ab", :fail => true
|
275
|
+
end
|
276
|
+
|
277
|
+
it "prints instructions" do
|
278
|
+
result = run "git pair ab", :fail => true
|
279
|
+
result.should include("Could not find a .pairs file. Create a YAML file in your project or home directory.")
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
context "but a .pairs file in the home directory" do
|
284
|
+
around do |example|
|
285
|
+
file = File.join(ENV["HOME"], ".pairs")
|
286
|
+
write file, <<-YAML.unindent
|
287
|
+
pairs:
|
288
|
+
ab: Aa Bb
|
289
|
+
bc: Bb Cc
|
290
|
+
cd: Cc Dd
|
291
|
+
|
292
|
+
email:
|
293
|
+
prefix: the-pair
|
294
|
+
domain: the-host.com
|
295
|
+
YAML
|
296
|
+
|
297
|
+
example.run
|
298
|
+
|
299
|
+
FileUtils.rm file
|
300
|
+
end
|
301
|
+
|
302
|
+
it "loads the file" do
|
303
|
+
result = run "git pair ab"
|
304
|
+
expect_config result, "Aa Bb", "ab", "the-pair+aa@the-host.com"
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe 'pair-commit' do
|
311
|
+
before do
|
312
|
+
write ".pairs", <<-YAML.unindent
|
313
|
+
pairs:
|
314
|
+
ab: Aa Bb; abb
|
315
|
+
bc: Bb Cc; bcc
|
316
|
+
cd: Cc Dd; cdd
|
317
|
+
|
318
|
+
email:
|
319
|
+
prefix: the-pair
|
320
|
+
domain: the-host.com
|
321
|
+
|
322
|
+
email_addresses:
|
323
|
+
bc: test@other-host.com
|
324
|
+
YAML
|
325
|
+
end
|
326
|
+
|
327
|
+
context 'when a pair has been set' do
|
328
|
+
before do
|
329
|
+
run "git pair ab cd"
|
330
|
+
end
|
331
|
+
|
332
|
+
def author_name_of_last_commit
|
333
|
+
(run "git log -1 --pretty=%an").strip
|
237
334
|
end
|
238
335
|
|
239
|
-
|
240
|
-
|
241
|
-
result.should include("Could not find a .pairs file. Create a YAML file in your project or home directory.")
|
336
|
+
def author_email_of_last_commit
|
337
|
+
(run "git log -1 --pretty=%ae").strip
|
242
338
|
end
|
339
|
+
|
340
|
+
def committer_name_of_last_commit
|
341
|
+
(run "git log -1 --pretty=%cn").strip
|
342
|
+
end
|
343
|
+
|
344
|
+
def committer_email_of_last_commit
|
345
|
+
(run "git log -1 --pretty=%ce").strip
|
346
|
+
end
|
347
|
+
|
348
|
+
it "makes a commit" do
|
349
|
+
git_pair_commit
|
350
|
+
output = run "git log -1"
|
351
|
+
output.should include("Pair pare pear")
|
352
|
+
end
|
353
|
+
|
354
|
+
it "sets the author name to the pair's names" do
|
355
|
+
git_pair_commit
|
356
|
+
output = run "git log -1 --pretty=%an"
|
357
|
+
output.strip.should eq("Aa Bb and Cc Dd")
|
358
|
+
end
|
359
|
+
|
360
|
+
it "randomly chooses from pair and sets user.email" do
|
361
|
+
emails = 6.times.map do
|
362
|
+
git_pair_commit
|
363
|
+
author_email_of_last_commit
|
364
|
+
end.uniq
|
365
|
+
emails.should =~ ['abb@the-host.com', 'cdd@the-host.com']
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'when git options are passed' do
|
369
|
+
it 'forwards those options to git' do
|
370
|
+
git_pair_commit
|
371
|
+
run 'git pair ab bc'
|
372
|
+
run 'git pair-commit --amend -C HEAD --reset-author'
|
373
|
+
|
374
|
+
output = run "git log -1 --pretty=%an"
|
375
|
+
output.strip.should eq("Aa Bb and Bb Cc")
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
context 'when the pair is set globally and the local repo has custom user name and email' do
|
380
|
+
before do
|
381
|
+
run 'git pair --global ab cd'
|
382
|
+
run "git config user.name 'Betty White'"
|
383
|
+
run "git config user.email 'betty@example.com'"
|
384
|
+
end
|
385
|
+
|
386
|
+
it 'still makes the commit with the correct user name' do
|
387
|
+
git_pair_commit
|
388
|
+
|
389
|
+
author_name_of_last_commit.should eq("Aa Bb and Cc Dd")
|
390
|
+
end
|
391
|
+
|
392
|
+
it 'still makes the commit with the correct user email' do
|
393
|
+
git_pair_commit
|
394
|
+
|
395
|
+
%w(abb@the-host.com cdd@the-host.com).should include(author_email_of_last_commit)
|
396
|
+
end
|
397
|
+
|
398
|
+
it 'still makes the commit with the correct committer name' do
|
399
|
+
git_pair_commit
|
400
|
+
|
401
|
+
committer_name_of_last_commit.should eq("Aa Bb and Cc Dd")
|
402
|
+
end
|
403
|
+
|
404
|
+
it 'still makes the commit with the correct committer email' do
|
405
|
+
git_pair_commit
|
406
|
+
|
407
|
+
%w(abb@the-host.com cdd@the-host.com).should include(committer_email_of_last_commit)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
context 'when one of the pair has a custom email address' do
|
412
|
+
before do
|
413
|
+
run 'git pair ab bc'
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'uses that email address' do
|
417
|
+
emails = 6.times.map do
|
418
|
+
git_pair_commit
|
419
|
+
author_email_of_last_commit
|
420
|
+
end.uniq
|
421
|
+
emails.should =~ ['abb@the-host.com', 'test@other-host.com']
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
context 'when no pair has been set' do
|
427
|
+
it 'raises an exception' do
|
428
|
+
git_pair_commit.should include('Error: No pair set')
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context 'when -h flag is passed' do
|
433
|
+
it 'shows the help message' do
|
434
|
+
results = run 'git pair-commit -h'
|
435
|
+
results.gsub(/\s+/, ' ').should include('randomly chooses the author email from the members of the pair')
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def git_pair_commit
|
440
|
+
run "echo #{rand(100)} > b"
|
441
|
+
run 'git add b'
|
442
|
+
run 'git pair-commit -m "Pair pare pear"', :fail => true
|
243
443
|
end
|
244
444
|
end
|
245
445
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'pivotal_git_scripts/git_pair'
|
2
|
+
|
3
|
+
describe PivotalGitScripts::GitPair::Runner do
|
4
|
+
let(:runner) { described_class.new }
|
5
|
+
|
6
|
+
describe 'set_git_config' do
|
7
|
+
it 'calls git config with pairs in the options' do
|
8
|
+
runner.should_receive(:system).with('git config user.foo "bar baz"')
|
9
|
+
|
10
|
+
runner.set_git_config(false, 'foo' => 'bar baz')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can unset git config options' do
|
14
|
+
runner.should_receive(:system).with('git config --unset user.foo')
|
15
|
+
|
16
|
+
runner.set_git_config(false, 'foo' => nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'can handle multiple pairs in a hash' do
|
20
|
+
runner.should_receive(:system).with('git config --unset user.remove')
|
21
|
+
runner.should_receive(:system).with('git config user.ten "10"')
|
22
|
+
|
23
|
+
runner.set_git_config(false, 'remove' => nil, 'ten' => '10')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'supports a global option' do
|
27
|
+
runner.should_receive(:system).with('git config --global user.foo "bar baz"')
|
28
|
+
|
29
|
+
runner.set_git_config(true, 'foo' => 'bar baz')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'read_author_info_from_config' do
|
34
|
+
it 'maps from the initials to the full name' do
|
35
|
+
config = {
|
36
|
+
'pairs' => {
|
37
|
+
'aa' => 'An Aardvark',
|
38
|
+
'tt' => 'The Turtle'
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
names = runner.read_author_info_from_config(config, ['aa', 'tt'])
|
43
|
+
names.should =~ ['An Aardvark', 'The Turtle']
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'exits when initials cannot be found' do
|
47
|
+
expect {
|
48
|
+
runner.read_author_info_from_config({"pairs" => {}}, ['aa'])
|
49
|
+
}.to raise_error(PivotalGitScripts::GitPair::GitPairException)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pivotal_git_scripts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.4.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Pivotal Labs
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-01-15 00:00:00.000000000 Z
|
13
12
|
dependencies: []
|
14
13
|
description: These scripts are helpers for managing developer workflow when using
|
15
14
|
git repos hosted on GitHub.
|
@@ -18,49 +17,53 @@ email:
|
|
18
17
|
executables:
|
19
18
|
- git-about
|
20
19
|
- git-pair
|
20
|
+
- git-pair-commit
|
21
21
|
- git-project
|
22
22
|
- git-superpull
|
23
23
|
extensions: []
|
24
24
|
extra_rdoc_files: []
|
25
25
|
files:
|
26
|
-
- .gitignore
|
27
|
-
- .rspec
|
26
|
+
- ".gitignore"
|
27
|
+
- ".rspec"
|
28
|
+
- ".travis.yml"
|
29
|
+
- CHANGELOG.md
|
28
30
|
- Gemfile
|
29
|
-
- Gemfile.lock
|
30
31
|
- MIT.LICENSE
|
31
32
|
- README.md
|
32
33
|
- Rakefile
|
33
34
|
- bin/git-about
|
34
35
|
- bin/git-pair
|
36
|
+
- bin/git-pair-commit
|
35
37
|
- bin/git-project
|
36
38
|
- bin/git-superpull
|
39
|
+
- lib/pivotal_git_scripts/git_pair.rb
|
37
40
|
- lib/pivotal_git_scripts/version.rb
|
38
41
|
- pivotal_git_scripts.gemspec
|
39
42
|
- spec/cli_spec.rb
|
43
|
+
- spec/git_pair_spec.rb
|
40
44
|
homepage: http://github.com/pivotal/git_scripts
|
41
45
|
licenses:
|
42
46
|
- MIT
|
47
|
+
metadata: {}
|
43
48
|
post_install_message:
|
44
49
|
rdoc_options: []
|
45
50
|
require_paths:
|
46
51
|
- lib
|
47
52
|
required_ruby_version: !ruby/object:Gem::Requirement
|
48
|
-
none: false
|
49
53
|
requirements:
|
50
|
-
- -
|
54
|
+
- - ">="
|
51
55
|
- !ruby/object:Gem::Version
|
52
56
|
version: '0'
|
53
57
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
58
|
requirements:
|
56
|
-
- -
|
59
|
+
- - ">="
|
57
60
|
- !ruby/object:Gem::Version
|
58
61
|
version: '0'
|
59
62
|
requirements: []
|
60
63
|
rubyforge_project: pivotal_git_scripts
|
61
|
-
rubygems_version:
|
64
|
+
rubygems_version: 2.2.2
|
62
65
|
signing_key:
|
63
|
-
specification_version:
|
66
|
+
specification_version: 4
|
64
67
|
summary: Developer git workflow convenience scripts
|
65
|
-
test_files:
|
66
|
-
|
68
|
+
test_files: []
|
69
|
+
has_rdoc:
|
data/Gemfile.lock
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
pivotal_git_scripts (1.1.4)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: http://rubygems.org/
|
8
|
-
specs:
|
9
|
-
diff-lcs (1.1.3)
|
10
|
-
rake (0.9.2.2)
|
11
|
-
rspec (2.8.0)
|
12
|
-
rspec-core (~> 2.8.0)
|
13
|
-
rspec-expectations (~> 2.8.0)
|
14
|
-
rspec-mocks (~> 2.8.0)
|
15
|
-
rspec-core (2.8.0)
|
16
|
-
rspec-expectations (2.8.0)
|
17
|
-
diff-lcs (~> 1.1.2)
|
18
|
-
rspec-mocks (2.8.0)
|
19
|
-
unindent (1.0)
|
20
|
-
|
21
|
-
PLATFORMS
|
22
|
-
ruby
|
23
|
-
|
24
|
-
DEPENDENCIES
|
25
|
-
bundler
|
26
|
-
pivotal_git_scripts!
|
27
|
-
rake
|
28
|
-
rspec
|
29
|
-
unindent
|