edsinclair-git-pair 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,5 @@
1
+ Autotest.add_hook :initialize do |autotest|
2
+ [/\.git/, /pkg\//, /\.gemspec$/, /\.log$/].each do |regexp|
3
+ autotest.add_exception regexp
4
+ end
5
+ end
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in git-pair.gemspec
4
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'git-pair'
4
+ require 'git-pair/command'
5
+
6
+ GitPair::Command.run!(ARGV)
@@ -0,0 +1,3 @@
1
+ default: --format progress --color features
2
+ autotest: --format pretty
3
+ autotest-all: --profile default
@@ -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
@@ -0,0 +1,9 @@
1
+ module GitPair
2
+ module Display
3
+ extend self
4
+
5
+ def git_author(authors)
6
+ "#{authors.map { |a| a.name }.join(' & ')} <#{Author.email(authors)}>"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module GitPair
2
+ VERSION = "0.3.0"
3
+ 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