edsinclair-git-pair 0.3.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.
- data/.autotest +5 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +57 -0
- data/Rakefile +19 -0
- data/bin/git-pair +6 -0
- data/config/cucumber.yml +3 -0
- data/edsinclair-git-pair.gemspec +23 -0
- data/features/adding_an_author.feature +27 -0
- data/features/authors.feature +33 -0
- data/features/removing_an_author.feature +19 -0
- data/features/resetting_the_pair.feature +11 -0
- data/features/setting_email_options.feature +8 -0
- data/features/step_definitions/config_steps.rb +120 -0
- data/features/support/env.rb +50 -0
- data/features/switching_authors.feature +45 -0
- data/hooks/post-commit +32 -0
- data/lib/git-pair.rb +11 -0
- data/lib/git-pair/author.rb +77 -0
- data/lib/git-pair/command.rb +152 -0
- data/lib/git-pair/config.rb +53 -0
- data/lib/git-pair/display.rb +9 -0
- data/lib/git-pair/version.rb +3 -0
- metadata +127 -0
data/.autotest
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Chris Kampmeier
|
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,57 @@
|
|
1
|
+
# git-pair
|
2
|
+
|
3
|
+
A git porcelain for changing `user.name` and `user.email` so you can commit as
|
4
|
+
more than one author.
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
Install the gem:
|
9
|
+
|
10
|
+
gem install edsinclair-git-pair
|
11
|
+
|
12
|
+
And here's how to use it!
|
13
|
+
|
14
|
+
$ git pair
|
15
|
+
|
16
|
+
General Syntax:
|
17
|
+
git pair [reset | authors | options]
|
18
|
+
|
19
|
+
Options:
|
20
|
+
-a, --add AUTHOR Add an author. Format: "Author Name <author@example.com>"
|
21
|
+
-r, --remove NAME Remove an author. Use the full name.
|
22
|
+
-d, --reset Reset current author to default (global) config
|
23
|
+
-s, --show 'aa [bb]' Show the string to be used for the commit author field
|
24
|
+
--install-hook Install a post-commit hook for the current repo. See git-pair/hooks/post-commit for more information.
|
25
|
+
--email EMAIL Add a default email address to be used for pairs
|
26
|
+
|
27
|
+
Switching authors:
|
28
|
+
git pair aa [bb] Where AA and BB are any abbreviation of an
|
29
|
+
author's name. You can specify one or more authors.
|
30
|
+
|
31
|
+
Current config:
|
32
|
+
Author list: Adam McCrea
|
33
|
+
Jon Distad
|
34
|
+
|
35
|
+
Current author: Jon Distad + Adam McCrea
|
36
|
+
Current email: devs+jd+am@edgecase.com
|
37
|
+
|
38
|
+
## How does it work?
|
39
|
+
|
40
|
+
The list of authors is maintained in the global git configuration file.
|
41
|
+
The current author is set in the git configuration local to the project.
|
42
|
+
The email address for a pair will be generated using the first author's email
|
43
|
+
domain and the initials separated by a '+' will precede the '@'.
|
44
|
+
Alternatively you can specify an email address to be used for pair commits.
|
45
|
+
For example: if you specify pair@example.net the pair email address will be pair+aa+bb@example.net
|
46
|
+
|
47
|
+
## About this version
|
48
|
+
|
49
|
+
This was forked from http://github.com/edgecase/git-pair which in turn was
|
50
|
+
forked from http://github.com/chrisk/git-pair. Many thanks to Chris Kampmeier
|
51
|
+
for the original version and Adam McCrea, John Distad and Ehren Murdick for the
|
52
|
+
features they added to the edgecase release. This version adds a --show, --email
|
53
|
+
and --install-hook options and converts the gem to use Bundler for dependency management.
|
54
|
+
|
55
|
+
## License
|
56
|
+
|
57
|
+
Copyright (c) 2009 Chris Kampmeier. See `LICENSE` for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler"
|
3
|
+
Bundler.setup
|
4
|
+
require 'bundler/gem_tasks'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'cucumber/rake/task'
|
8
|
+
Cucumber::Rake::Task.new(:features)
|
9
|
+
task :features
|
10
|
+
rescue LoadError
|
11
|
+
task :features do
|
12
|
+
abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :features
|
17
|
+
|
18
|
+
# Don't print commands when shelling out (for example, running Cucumber)
|
19
|
+
RakeFileUtils.verbose(false)
|
data/bin/git-pair
ADDED
data/config/cucumber.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "git-pair/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "edsinclair-git-pair"
|
7
|
+
s.version = GitPair::VERSION
|
8
|
+
s.authors = ["Chris Kampmeier", "Adam McCrea", "Jon Distad", "Eirik Dentz Sinclair", "Tim Gildea"]
|
9
|
+
s.email = ["eirik@efficiency20.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.date = %q{2011-08-24}
|
12
|
+
s.summary = %q{Configure git to commit as more than one author}
|
13
|
+
s.description = %q{A git porcelain for pair programming. Changes git-config's user.name and user.email settings so you can commit as more than one author.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "git-pair"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.add_development_dependency "cucumber"
|
22
|
+
s.add_development_dependency "ruby-debug"
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Adding an author
|
2
|
+
In order to commit as a pair
|
3
|
+
A user should be able to
|
4
|
+
add a name and email to the list of authors
|
5
|
+
|
6
|
+
Scenario: adding a name and email
|
7
|
+
When I add the author "Linus Torvalds <linus@example.org>"
|
8
|
+
Then `git pair` should display "Linus Torvalds" in its author list
|
9
|
+
|
10
|
+
Scenario: adding the same name and email twice
|
11
|
+
When I add the author "Linus Torvalds <linus@example.org>"
|
12
|
+
And I add the author "Linus Torvalds <linus@example.org>"
|
13
|
+
Then `git pair` should display "Linus Torvalds" in its author list only once
|
14
|
+
And the gitconfig should include "Linus Torvalds" in its author list only once
|
15
|
+
|
16
|
+
Scenario: adding the same name twice with different emails
|
17
|
+
When I add the author "Linus Torvalds <linus@example.org>"
|
18
|
+
And I add the author "Linus Torvalds <linus@example.com>"
|
19
|
+
Then `git pair` should display "Linus Torvalds" in its author list only once
|
20
|
+
And the gitconfig should include "Linus Torvalds" in its author list only once
|
21
|
+
And the gitconfig should include "linus@example.org" as the email of "Linus Torvalds"
|
22
|
+
|
23
|
+
Scenario: adding a malformed author string
|
24
|
+
When I add the author " "
|
25
|
+
And I add the author "Bob Dole"
|
26
|
+
And I add the author "Jimmy <asdf"
|
27
|
+
Then the config file should have no authors
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Feature: seeing authors on console
|
2
|
+
In order to see the current author
|
3
|
+
A user should be able to
|
4
|
+
get the author string for a set of initials
|
5
|
+
|
6
|
+
Scenario: No authors have been added
|
7
|
+
When I specify the initials "AA BB"
|
8
|
+
Then the last command's output should include "Please add some authors first"
|
9
|
+
|
10
|
+
Scenario: A single author
|
11
|
+
Given I have added the author "Linus Torvalds <linus@example.net>"
|
12
|
+
When I specify the initials "LT"
|
13
|
+
Then the last command's output should include "Linus Torvalds <linus@example.net>"
|
14
|
+
|
15
|
+
Scenario: Two authors
|
16
|
+
Given I have added the author "Linus Torvalds <linus@example.net>"
|
17
|
+
And I have added the author "Junio C Hamano <junio@example.org>"
|
18
|
+
And my global Git configuration is setup with email "devs@example.com"
|
19
|
+
When I specify the initials "LT JCH"
|
20
|
+
Then the last command's output should include "Linus Torvalds & Junio C Hamano <lt+jch@example.net>"
|
21
|
+
|
22
|
+
Scenario: A single author and the pair email has been set
|
23
|
+
Given I have set the pair email to "devs@widgets.com"
|
24
|
+
And I have added the author "Linus Torvalds <linus@example.net>"
|
25
|
+
When I specify the initials "LT"
|
26
|
+
Then the last command's output should include "Linus Torvalds <linus@example.net>"
|
27
|
+
|
28
|
+
Scenario: Pairing with two authors and the pair email has been set
|
29
|
+
Given I have set the pair email to "devs@widgets.com"
|
30
|
+
And I have added the author "Linus Torvalds <linus@example.net>"
|
31
|
+
And I have added the author "Junio C Hamano <junio@example.org>"
|
32
|
+
When I specify the initials "LT JCH"
|
33
|
+
Then the last command's output should include "Linus Torvalds & Junio C Hamano <devs+lt+jch@widgets.com>"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Feature: Adding an author
|
2
|
+
In order remove old pairing partners
|
3
|
+
A user should be able to
|
4
|
+
remove a name from the list of authors
|
5
|
+
|
6
|
+
Scenario: removing a name
|
7
|
+
When I add the author "Linus Torvalds <linus@example.org>"
|
8
|
+
And I add the author "Junio C Hamano <junio@example.org>"
|
9
|
+
And I remove the name "Junio C Hamano"
|
10
|
+
Then `git pair` should display the following author list:
|
11
|
+
| name |
|
12
|
+
| Linus Torvalds |
|
13
|
+
|
14
|
+
Scenario: removing all names
|
15
|
+
When I add the author "Linus Torvalds <linus@example.org>"
|
16
|
+
And I add the author "Junio C Hamano <junio@example.org>"
|
17
|
+
And I remove the name "Linus Torvalds"
|
18
|
+
And I remove the name "Junio C Hamano"
|
19
|
+
Then `git pair` should display an empty author list
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Feature: Resetting the pair
|
2
|
+
In order reset to the global author
|
3
|
+
A user should be able to
|
4
|
+
reset to the Global User
|
5
|
+
|
6
|
+
Scenario: resetting the current authors
|
7
|
+
Given I have added the author "Linus Torvalds <linus@example.org>"
|
8
|
+
And my global Git configuration is setup with user "Global User"
|
9
|
+
And I switch to the pair "LT"
|
10
|
+
When I reset the current authors
|
11
|
+
Then `git pair` should display "Global User" for the current author
|
@@ -0,0 +1,8 @@
|
|
1
|
+
Feature: Setting a pair email
|
2
|
+
In order to commit as a pair
|
3
|
+
A user should be able to
|
4
|
+
set a pair email address
|
5
|
+
|
6
|
+
Scenario: setting a pair email
|
7
|
+
When I set the pair email to "pair@widgets.com"
|
8
|
+
Then `git pair` should display "pair@widgets.com" as its pair email
|
@@ -0,0 +1,120 @@
|
|
1
|
+
Given /^I have added the author "([^\"]*)"$/ do |name_and_email|
|
2
|
+
When %(I add the author "#{name_and_email}")
|
3
|
+
end
|
4
|
+
|
5
|
+
Given /^my global Git configuration is setup with user "([^\"]*)"$/ do |name|
|
6
|
+
git_config "--global user.name \"#{name}\""
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^my global Git configuration is setup with email "([^\"]*)"$/ do |email|
|
10
|
+
git_config "--global user.email \"#{email}\""
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^I (?:have )?set the pair email to "([^"]*)"$/ do |email|
|
14
|
+
git_pair %(--email "#{email}")
|
15
|
+
end
|
16
|
+
|
17
|
+
Then /^`git pair` should display "([^"]*)" as its pair email$/ do |email|
|
18
|
+
output = git_pair
|
19
|
+
assert_equal email, current_pair_email_from_output(output)
|
20
|
+
end
|
21
|
+
|
22
|
+
When /^I add the author "([^\"]*)"$/ do |name_and_email|
|
23
|
+
git_pair %(--add "#{name_and_email}")
|
24
|
+
end
|
25
|
+
|
26
|
+
When /^I remove the name "([^\"]*)"$/ do |name|
|
27
|
+
git_pair %(--remove "#{name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
When /^I specify the initials "([^"]*)"$/ do |abbreviations|
|
31
|
+
@output = git_pair %(--show "#{abbreviations}")
|
32
|
+
end
|
33
|
+
|
34
|
+
When /^I (?:try to )?switch to the pair "([^\"]*)"$/ do |abbreviations|
|
35
|
+
@output = git_pair abbreviations
|
36
|
+
end
|
37
|
+
|
38
|
+
When /^I reset the current authors$/ do
|
39
|
+
git_pair '--reset'
|
40
|
+
end
|
41
|
+
|
42
|
+
Then /^`git pair` should display "([^\"]*)" in its author list$/ do |name|
|
43
|
+
output = git_pair
|
44
|
+
authors = authors_list_from_output(output)
|
45
|
+
assert authors.include?(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
Then /^`git pair` should display "([^\"]*)" in its author list only once$/ do |name|
|
49
|
+
output = git_pair
|
50
|
+
authors = authors_list_from_output(output)
|
51
|
+
assert_equal 1, authors.select { |author| author == name}.size
|
52
|
+
end
|
53
|
+
|
54
|
+
Then /^`git pair` should display no authors$/ do
|
55
|
+
output = git_pair
|
56
|
+
authors = authors_list_from_output(output)
|
57
|
+
authors.size.should be_zero
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
Then /^`git pair` should display "([^\"]*)" for the current author$/ do |names|
|
62
|
+
output = git_pair
|
63
|
+
assert_equal names, current_author_from_output(output)
|
64
|
+
end
|
65
|
+
|
66
|
+
Then /^`git pair` should display "([^\"]*)" for the current email$/ do |email|
|
67
|
+
output = git_pair
|
68
|
+
assert_equal email, current_email_from_output(output)
|
69
|
+
end
|
70
|
+
|
71
|
+
Then /^the gitconfig should include "([^\"]*)" in its author list only once$/ do |name|
|
72
|
+
output = git_config
|
73
|
+
authors = output.split("\n").map { |line| line =~ /^git-pair\.authors=(.*) <[^>]+>$/; $1 }.compact
|
74
|
+
assert_equal 1, authors.select { |author| author == name}.size
|
75
|
+
end
|
76
|
+
|
77
|
+
Then /^the gitconfig should include "([^\"]*)" as the email of "([^\"]*)"$/ do |email, name|
|
78
|
+
output = git_config
|
79
|
+
authors = output.split("\n").map { |line| line =~ /^git-pair\.authors=.* <([^>]+)>$/; $1 }.compact
|
80
|
+
assert_equal 1, authors.select { |author| author == email}.size
|
81
|
+
end
|
82
|
+
|
83
|
+
Then /^`git pair` should display the following author list:$/ do |table|
|
84
|
+
output = git_pair
|
85
|
+
names = authors_list_from_output(output).map { |name| {"name" => name} }
|
86
|
+
table.diff! names
|
87
|
+
end
|
88
|
+
|
89
|
+
Then /^`git pair` should display an empty author list$/ do
|
90
|
+
output = git_pair
|
91
|
+
assert authors_list_from_output(output).empty?
|
92
|
+
end
|
93
|
+
|
94
|
+
Then /^the last command's output should include "([^\"]*)"$/ do |output|
|
95
|
+
assert @output.include?(output)
|
96
|
+
end
|
97
|
+
|
98
|
+
Then /^the config file should have no authors$/ do
|
99
|
+
assert git_config(%(--global --get-all git-pair.authors)).empty?
|
100
|
+
end
|
101
|
+
|
102
|
+
def current_pair_email_from_output(output)
|
103
|
+
output =~ /Pair email: (.*?)\n/im
|
104
|
+
$1.strip
|
105
|
+
end
|
106
|
+
|
107
|
+
def authors_list_from_output(output)
|
108
|
+
output =~ /Author list: (.*?)\n\s?\n/im
|
109
|
+
$1.strip.split("\n").map { |name| name.strip }
|
110
|
+
end
|
111
|
+
|
112
|
+
def current_author_from_output(output)
|
113
|
+
output =~ /Current author: (.*?)\n/im
|
114
|
+
$1.strip
|
115
|
+
end
|
116
|
+
|
117
|
+
def current_email_from_output(output)
|
118
|
+
output =~ /Current email: (.*?)\n/im
|
119
|
+
$1.strip
|
120
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'test/unit/assertions'
|
3
|
+
World(Test::Unit::Assertions)
|
4
|
+
|
5
|
+
|
6
|
+
module RepositoryHelper
|
7
|
+
# TODO: use 1.8.7's Dir.mktmpdir?
|
8
|
+
TEST_REPO_PATH = File.join(Dir::tmpdir, "git-pair-test-repo")
|
9
|
+
TEST_REPO_DOT_GIT_PATH = "#{TEST_REPO_PATH}/.git"
|
10
|
+
|
11
|
+
PROJECT_PATH = File.join(File.dirname(__FILE__), "../..")
|
12
|
+
GIT_PAIR = "#{PROJECT_PATH}/bin/git-pair"
|
13
|
+
CONFIG_BACKUP_PATH = "#{PROJECT_PATH}/tmp"
|
14
|
+
|
15
|
+
def git_pair(options = "")
|
16
|
+
output = `HOME=#{TEST_REPO_PATH} GIT_DIR=#{TEST_REPO_DOT_GIT_PATH} #{GIT_PAIR} #{options} 2>&1`
|
17
|
+
output.gsub(/\e\[\d\d?m/, '') # strip any ANSI colors
|
18
|
+
end
|
19
|
+
|
20
|
+
def git_config(options = nil)
|
21
|
+
options ||= "--list"
|
22
|
+
`HOME=#{TEST_REPO_PATH} GIT_DIR=#{TEST_REPO_DOT_GIT_PATH} git config #{options} 2>&1`
|
23
|
+
end
|
24
|
+
|
25
|
+
def backup_gitconfigs
|
26
|
+
FileUtils.mkdir_p CONFIG_BACKUP_PATH
|
27
|
+
FileUtils.cp File.expand_path("~/.gitconfig"), "#{CONFIG_BACKUP_PATH}/.gitconfig.backup"
|
28
|
+
FileUtils.cp "#{PROJECT_PATH}/.git/config", "#{CONFIG_BACKUP_PATH}/config.backup"
|
29
|
+
end
|
30
|
+
|
31
|
+
def restore_gitconfigs
|
32
|
+
FileUtils.cp "#{CONFIG_BACKUP_PATH}/config.backup", "#{PROJECT_PATH}/.git/config"
|
33
|
+
FileUtils.cp "#{CONFIG_BACKUP_PATH}/.gitconfig.backup", File.expand_path("~/.gitconfig")
|
34
|
+
FileUtils.rm_rf CONFIG_BACKUP_PATH
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
World(RepositoryHelper)
|
39
|
+
|
40
|
+
|
41
|
+
Before do
|
42
|
+
backup_gitconfigs
|
43
|
+
FileUtils.mkdir_p RepositoryHelper::TEST_REPO_DOT_GIT_PATH
|
44
|
+
`GIT_DIR=#{RepositoryHelper::TEST_REPO_DOT_GIT_PATH} && git init`
|
45
|
+
end
|
46
|
+
|
47
|
+
After do
|
48
|
+
FileUtils.rm_rf RepositoryHelper::TEST_REPO_PATH
|
49
|
+
restore_gitconfigs
|
50
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Feature: Switching authors
|
2
|
+
In order to indicate which authors are committing
|
3
|
+
A user should be able to
|
4
|
+
change the currently active pair
|
5
|
+
|
6
|
+
Scenario: No authors have been added
|
7
|
+
When I try to switch to the pair "AA BB"
|
8
|
+
Then the last command's output should include "Please add some authors first"
|
9
|
+
|
10
|
+
Scenario: Pairing with a single author
|
11
|
+
Given I have added the author "Linus Torvalds <linus@example.net>"
|
12
|
+
When I switch to the pair "LT"
|
13
|
+
Then `git pair` should display "Linus Torvalds" for the current author
|
14
|
+
And `git pair` should display "linus@example.net" for the current email
|
15
|
+
|
16
|
+
Scenario: Pairing with two authors
|
17
|
+
Given I have added the author "Linus Torvalds <linus@example.net>"
|
18
|
+
And I have added the author "Junio C Hamano <junio@example.org>"
|
19
|
+
And my global Git configuration is setup with email "devs@example.com"
|
20
|
+
When I switch to the pair "LT JCH"
|
21
|
+
Then `git pair` should display "Linus Torvalds & Junio C Hamano" for the current author
|
22
|
+
And `git pair` should display "lt+jch@example.net" for the current email
|
23
|
+
|
24
|
+
Scenario: Pairing with two authors the pair email domain is set to the first author
|
25
|
+
Given I have added the author "Linus Torvalds <linus@example.net>"
|
26
|
+
And I have added the author "Junio C Hamano <junio@example.org>"
|
27
|
+
And my global Git configuration is setup with email "devs@example.com"
|
28
|
+
When I switch to the pair "JCH LT"
|
29
|
+
Then `git pair` should display "Junio C Hamano & Linus Torvalds" for the current author
|
30
|
+
And `git pair` should display "jch+lt@example.org" for the current email
|
31
|
+
|
32
|
+
Scenario: Pairing with a single author and the pair email has been set
|
33
|
+
Given I have set the pair email to "devs@widgets.com"
|
34
|
+
And I have added the author "Linus Torvalds <linus@example.net>"
|
35
|
+
When I switch to the pair "LT"
|
36
|
+
Then `git pair` should display "Linus Torvalds" for the current author
|
37
|
+
And `git pair` should display "linus@example.net" for the current email
|
38
|
+
|
39
|
+
Scenario: Pairing with two authors and the pair email has been set
|
40
|
+
Given I have set the pair email to "devs@widgets.com"
|
41
|
+
And I have added the author "Linus Torvalds <linus@example.net>"
|
42
|
+
And I have added the author "Junio C Hamano <junio@example.org>"
|
43
|
+
When I switch to the pair "LT JCH"
|
44
|
+
Then `git pair` should display "Linus Torvalds & Junio C Hamano" for the current author
|
45
|
+
And `git pair` should display "devs+lt+jch@widgets.com" for the current email
|
data/hooks/post-commit
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This post-commit hook looks for one or more initials inside parentheses at the end of
|
4
|
+
# the commit message and uses them to amend the commit author field using the git pair authors
|
5
|
+
# matching those initials.
|
6
|
+
#
|
7
|
+
# To enable this hook, add a symlink to project/.git/hooks/post-commit that points to this file
|
8
|
+
|
9
|
+
revision, author, body = %x[git log HEAD -1 --format="%H<separator>%an <%ae><separator>%B"].split("<separator>")
|
10
|
+
|
11
|
+
pair_regex = Regexp.new(/^([^(]*)\(([^)]*)\)$/)
|
12
|
+
initials_regex = Regexp.new(/\/|\s|,/)
|
13
|
+
|
14
|
+
if body.match(pair_regex)
|
15
|
+
message = $1.strip # raw commit message without initials
|
16
|
+
initials = $2.split(initials_regex)
|
17
|
+
|
18
|
+
git_pair_author = %x[git pair -s "#{initials.join(' ')}"].chomp
|
19
|
+
git_amend_author = %Q(git commit --amend --author="#{git_pair_author}" --message="#{message}")
|
20
|
+
|
21
|
+
if git_pair_author.empty?
|
22
|
+
puts "One or more of the initials: (#{initials.join(',')}) didn't match any of the git pair authors."
|
23
|
+
puts "You can use the following command to manually amend the HEAD commit:\n\n"
|
24
|
+
puts %Q( git commit --amend --author="First Last & First Last <pair+fl+fl@example.com>"\n\n)
|
25
|
+
elsif git_pair_author != author
|
26
|
+
puts "Amending revision: #{revision} author from: #{author}"
|
27
|
+
puts "To: #{git_pair_author}"
|
28
|
+
system(git_amend_author)
|
29
|
+
new_sha = %x[git rev-parse HEAD].chomp
|
30
|
+
puts "Succeeded! New SHA is: #{new_sha}"
|
31
|
+
end
|
32
|
+
end
|
data/lib/git-pair.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "git-pair/version"
|
2
|
+
|
3
|
+
module GitPair
|
4
|
+
autoload :Command, 'git-pair/command'
|
5
|
+
autoload :Author, 'git-pair/author'
|
6
|
+
autoload :Config, 'git-pair/config'
|
7
|
+
autoload :Display, 'git-pair/display'
|
8
|
+
|
9
|
+
class NoMatchingAuthorsError < ArgumentError; end
|
10
|
+
class MissingConfigurationError < RuntimeError; end
|
11
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module GitPair
|
2
|
+
class Author
|
3
|
+
|
4
|
+
ValidAuthorStringRegex = /^\s*([^<]+)<([^>]+)>\s*$/
|
5
|
+
|
6
|
+
class InvalidAuthorString < TypeError; end
|
7
|
+
|
8
|
+
def self.all
|
9
|
+
Config.all_author_strings.map { |string| new(string) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find_all(abbrs)
|
13
|
+
raise MissingConfigurationError, "Please add some authors first" if all.empty?
|
14
|
+
abbrs.map { |abbr| self.find(abbr) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.find(abbr)
|
18
|
+
all.find { |author| author.match?(abbr) } ||
|
19
|
+
raise(NoMatchingAuthorsError, "no authors matched #{abbr}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.email(authors)
|
23
|
+
if authors.length == 1
|
24
|
+
authors.first.email
|
25
|
+
else
|
26
|
+
author_names = authors.map { |a| a.initials }
|
27
|
+
if self.authors_prefix
|
28
|
+
author_names.unshift(authors_prefix)
|
29
|
+
end
|
30
|
+
initials_string = author_names.join('+')
|
31
|
+
"#{initials_string}@#{authors_email(authors)}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.authors_prefix
|
36
|
+
return Config.pair_email.split("@").first unless Config.pair_email.empty?
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.authors_email(authors)
|
40
|
+
return Config.pair_email.split("@").last unless Config.pair_email.empty?
|
41
|
+
return authors.first.email.split("@").last
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.exists?(author)
|
45
|
+
self.all.find { |a| a.name == author.name }
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.valid_string?(author_string)
|
49
|
+
author_string =~ ValidAuthorStringRegex
|
50
|
+
end
|
51
|
+
|
52
|
+
attr_reader :name, :email
|
53
|
+
|
54
|
+
def initialize(string)
|
55
|
+
unless Author.valid_string?(string)
|
56
|
+
raise(InvalidAuthorString, "\"#{string}\" is not a valid name and email")
|
57
|
+
end
|
58
|
+
|
59
|
+
string =~ ValidAuthorStringRegex
|
60
|
+
@name = $1.to_s.strip
|
61
|
+
@email = $2.to_s.strip
|
62
|
+
end
|
63
|
+
|
64
|
+
def <=>(other)
|
65
|
+
name.split.last <=> other.name.split.last
|
66
|
+
end
|
67
|
+
|
68
|
+
def initials
|
69
|
+
name.split.map { |word| word[0].chr }.join.downcase
|
70
|
+
end
|
71
|
+
|
72
|
+
def match?(abbr)
|
73
|
+
abbr.downcase == initials
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module GitPair
|
6
|
+
module Command
|
7
|
+
extend self
|
8
|
+
|
9
|
+
C_BOLD, C_REVERSE, C_RED, C_RESET = "\e[1m", "\e[7m", "\e[91m", "\e[0m"
|
10
|
+
|
11
|
+
def run!(args)
|
12
|
+
options = OpenStruct.new(:update => true,
|
13
|
+
:authors => [])
|
14
|
+
|
15
|
+
parser = OptionParser.new do |opts|
|
16
|
+
opts.banner = highlight('General Syntax:')
|
17
|
+
opts.separator ' git pair [reset | authors | options]'
|
18
|
+
|
19
|
+
opts.separator ' '
|
20
|
+
opts.separator highlight('Options:')
|
21
|
+
|
22
|
+
opts.on '-a', '--add AUTHOR', 'Add an author. Format: "Author Name <author@example.com>"' do |author|
|
23
|
+
options.add_author = Author.new(author)
|
24
|
+
Config.add_author(options.add_author)
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on '-r', '--remove NAME', 'Remove an author. Use the full name.' do |name|
|
28
|
+
options.remove_author = name
|
29
|
+
Config.remove_author options.remove_author
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on '-d', '--reset', 'Reset current author to default (global) config' do
|
33
|
+
options.reset = true
|
34
|
+
Config.reset
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on '--install-hook', 'Install a post-commit hook for the current repo. See git-pair/hooks/post-commit for more information.' do
|
38
|
+
options.symlink = true
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on '--email EMAIL', 'Add a default email address to be used for pairs' do |email|
|
42
|
+
puts "Setting email to #{email}"
|
43
|
+
options.pair_email = email
|
44
|
+
Config.set_pair_email(options.pair_email)
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on '-s', "--show 'aa [bb]'", 'Show the string to be used for the commit author field' do |initials|
|
48
|
+
options.update = false
|
49
|
+
options.authors = Author.find_all(initials.split(' '))
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.separator ' '
|
53
|
+
opts.separator highlight('Switching authors:')
|
54
|
+
opts.separator ' git pair aa [bb] Where AA and BB are any abbreviation of an'
|
55
|
+
opts.separator ' '*37 + 'author\'s name. You can specify one or more authors.'
|
56
|
+
|
57
|
+
opts.separator ' '
|
58
|
+
opts.separator highlight('Current config:')
|
59
|
+
opts.separator author_list.split("\n")
|
60
|
+
opts.separator ' '
|
61
|
+
opts.separator pair_email.split("\n")
|
62
|
+
opts.separator ' '
|
63
|
+
opts.separator current_author_info.split("\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
initials = parser.parse!(args.dup)
|
67
|
+
|
68
|
+
initials = initials.map { |e| e.split(' ') }.flatten # in case initials are enclosed in quotes
|
69
|
+
|
70
|
+
if options.authors.empty? && !initials.empty?
|
71
|
+
options.authors = Author.find_all(initials)
|
72
|
+
end
|
73
|
+
|
74
|
+
if args.empty?
|
75
|
+
puts parser.help
|
76
|
+
elsif options.authors && !options.update
|
77
|
+
puts Display.git_author options.authors
|
78
|
+
elsif options.symlink
|
79
|
+
symlink_post_commit_hook
|
80
|
+
elsif options.authors.empty?
|
81
|
+
puts author_list
|
82
|
+
puts
|
83
|
+
puts current_author_info
|
84
|
+
else
|
85
|
+
Config.switch(options.authors)
|
86
|
+
puts current_author_info
|
87
|
+
end
|
88
|
+
|
89
|
+
rescue OptionParser::MissingArgument
|
90
|
+
abort "missing required argument", parser.help
|
91
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => e
|
92
|
+
abort e.message.sub(':', ''), parser.help
|
93
|
+
rescue NoMatchingAuthorsError => e
|
94
|
+
abort e.message, "\n" + author_list
|
95
|
+
rescue MissingConfigurationError => e
|
96
|
+
abort e.message, parser.help
|
97
|
+
rescue Author::InvalidAuthorString => e
|
98
|
+
abort e.message, parser.help
|
99
|
+
end
|
100
|
+
|
101
|
+
def author_list
|
102
|
+
" #{bold 'Author list:'} #{Author.all.sort.map { |a| a.name }.join "\n "}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def pair_email
|
106
|
+
" #{bold 'Pair email:'} #{Config.pair_email} \n"
|
107
|
+
end
|
108
|
+
|
109
|
+
def current_author_info
|
110
|
+
" #{bold 'Current author:'} #{Config.current_author}\n" +
|
111
|
+
" #{bold 'Current email:'} #{Config.current_email}\n "
|
112
|
+
end
|
113
|
+
|
114
|
+
def abort(error_message, extra = "")
|
115
|
+
super red(" Error: #{error_message}\n") + extra
|
116
|
+
end
|
117
|
+
|
118
|
+
def highlight(string)
|
119
|
+
"#{C_REVERSE}#{string}#{C_RESET}"
|
120
|
+
end
|
121
|
+
|
122
|
+
def bold(string)
|
123
|
+
"#{C_BOLD}#{string}#{C_RESET}"
|
124
|
+
end
|
125
|
+
|
126
|
+
def red(string)
|
127
|
+
"#{C_RED}#{C_REVERSE}#{string}#{C_RESET}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def symlink_post_commit_hook
|
131
|
+
this_directory = Pathname.new File.expand_path(File.dirname(__FILE__))
|
132
|
+
post_commit_hook = Pathname.new(File.join(this_directory, "..", "..", "hooks", "post-commit")).realpath
|
133
|
+
project_git_hooks = File.join(Dir.pwd, ".git", "hooks")
|
134
|
+
|
135
|
+
if File.exists?(post_commit_hook) && File.exists?(project_git_hooks)
|
136
|
+
symlink_target = Pathname.new(File.join(project_git_hooks, "post-commit"))
|
137
|
+
|
138
|
+
puts "Symlinking: #{symlink_target}\nto #{post_commit_hook}.\n\n"
|
139
|
+
|
140
|
+
if File.exists?(symlink_target)
|
141
|
+
puts "Can't create symlink! #{symlink_target} already exists."
|
142
|
+
else
|
143
|
+
symlink_target.make_symlink(post_commit_hook)
|
144
|
+
puts "Succceded!"
|
145
|
+
end
|
146
|
+
elsif !File.exists?(project_git_hooks)
|
147
|
+
puts "The current directory doesn't appear to be a valid git repository."
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module GitPair
|
2
|
+
module Config
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def all_author_strings
|
6
|
+
`git config --global --get-all git-pair.authors`.split("\n")
|
7
|
+
end
|
8
|
+
|
9
|
+
def all_author_strings
|
10
|
+
`git config --global --get-all git-pair.authors`.split("\n")
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_author(author)
|
14
|
+
unless Author.exists?(author)
|
15
|
+
`git config --global --add git-pair.authors "#{author.name} <#{author.email}>"`
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def remove_author(name)
|
20
|
+
`git config --global --unset-all git-pair.authors "^#{name} <"`
|
21
|
+
`git config --global --remove-section git-pair` if all_author_strings.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def switch(authors)
|
25
|
+
`git config user.name "#{authors.map { |a| a.name }.join(' & ')}"`
|
26
|
+
`git config user.email "#{Author.email(authors)}"`
|
27
|
+
end
|
28
|
+
|
29
|
+
def reset
|
30
|
+
`git config --remove-section user`
|
31
|
+
end
|
32
|
+
|
33
|
+
def default_email
|
34
|
+
`git config --global --get user.email`.strip
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_author
|
38
|
+
`git config --get user.name`.strip
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_email
|
42
|
+
`git config --get user.email`.strip
|
43
|
+
end
|
44
|
+
|
45
|
+
def pair_email
|
46
|
+
`git config --global --get git-pair.email`.strip
|
47
|
+
end
|
48
|
+
|
49
|
+
def set_pair_email(email)
|
50
|
+
`git config --global git-pair.email "#{email}"`
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: edsinclair-git-pair
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Chris Kampmeier
|
14
|
+
- Adam McCrea
|
15
|
+
- Jon Distad
|
16
|
+
- Eirik Dentz Sinclair
|
17
|
+
- Tim Gildea
|
18
|
+
autorequire:
|
19
|
+
bindir: bin
|
20
|
+
cert_chain: []
|
21
|
+
|
22
|
+
date: 2011-08-24 00:00:00 Z
|
23
|
+
dependencies:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
prerelease: false
|
26
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
hash: 3
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
version: "0"
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
name: cucumber
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :development
|
50
|
+
version_requirements: *id002
|
51
|
+
name: ruby-debug
|
52
|
+
description: A git porcelain for pair programming. Changes git-config's user.name and user.email settings so you can commit as more than one author.
|
53
|
+
email:
|
54
|
+
- eirik@efficiency20.com
|
55
|
+
executables:
|
56
|
+
- git-pair
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
extra_rdoc_files: []
|
60
|
+
|
61
|
+
files:
|
62
|
+
- .autotest
|
63
|
+
- .gitignore
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/git-pair
|
69
|
+
- config/cucumber.yml
|
70
|
+
- edsinclair-git-pair.gemspec
|
71
|
+
- features/adding_an_author.feature
|
72
|
+
- features/authors.feature
|
73
|
+
- features/removing_an_author.feature
|
74
|
+
- features/resetting_the_pair.feature
|
75
|
+
- features/setting_email_options.feature
|
76
|
+
- features/step_definitions/config_steps.rb
|
77
|
+
- features/support/env.rb
|
78
|
+
- features/switching_authors.feature
|
79
|
+
- hooks/post-commit
|
80
|
+
- lib/git-pair.rb
|
81
|
+
- lib/git-pair/author.rb
|
82
|
+
- lib/git-pair/command.rb
|
83
|
+
- lib/git-pair/config.rb
|
84
|
+
- lib/git-pair/display.rb
|
85
|
+
- lib/git-pair/version.rb
|
86
|
+
homepage: ""
|
87
|
+
licenses: []
|
88
|
+
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options: []
|
91
|
+
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
requirements: []
|
113
|
+
|
114
|
+
rubyforge_project: git-pair
|
115
|
+
rubygems_version: 1.8.8
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Configure git to commit as more than one author
|
119
|
+
test_files:
|
120
|
+
- features/adding_an_author.feature
|
121
|
+
- features/authors.feature
|
122
|
+
- features/removing_an_author.feature
|
123
|
+
- features/resetting_the_pair.feature
|
124
|
+
- features/setting_email_options.feature
|
125
|
+
- features/step_definitions/config_steps.rb
|
126
|
+
- features/support/env.rb
|
127
|
+
- features/switching_authors.feature
|