git-duet 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ /.env
3
+ Gemfile.lock
4
+ /coverage/
5
+ /tags
@@ -0,0 +1 @@
1
+ compat.version=1.9
@@ -0,0 +1 @@
1
+ 1.9.3-p327
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,5 @@
1
+ if ENV['COVERAGE']
2
+ SimpleCov.start do
3
+ add_filter '/spec/'
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-head
7
+ - ree
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 ModCloth, Inc.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,268 @@
1
+ # Git Duet
2
+
3
+ <a href="https://travis-ci.org/modcloth/git-duet">
4
+ <img src="https://api.travis-ci.org/modcloth/git-duet.png"
5
+ title="Build Status"
6
+ /></a>
7
+
8
+ Pair harmoniously! Working in a pair doesn't mean you've both lost your
9
+ identity. Git Duet helps with blaming/praising by using stuff that's
10
+ already in `git` without littering your repo history with fictitous user
11
+ identities.
12
+
13
+ ## Installation
14
+
15
+ Install it with `gem`:
16
+
17
+ ~~~~~ bash
18
+ gem install git-duet
19
+ ~~~~~
20
+
21
+ ## Usage
22
+
23
+ ### Setup
24
+
25
+ Make an authors file with email domain, or if you're already using
26
+ [git pair](https://github.com/pivotal/git_scripts), just symlink your
27
+ `~/.pairs` file over to `~/.git-authors`.
28
+
29
+ ~~~~~ yaml
30
+ authors:
31
+ jd: Jane Doe; jane
32
+ fb: Frances Bar
33
+ email:
34
+ domain: awesometown.local
35
+ ~~~~~
36
+
37
+ `git duet` will use the `git pair` YAML structure if it has to (the
38
+ difference is the top-level key being `pairs` instead of `authors`,) e.g.:
39
+
40
+ ~~~~~ yaml
41
+ pairs:
42
+ jd: Jane Doe; jane
43
+ fb: Frances Bar
44
+ email:
45
+ domain: awesometown.local
46
+ ~~~~~
47
+
48
+ If you want your authors file to live somwhere else, just tell
49
+ Git Duet about it via the `GIT_DUET_AUTHORS_FILE` environmental
50
+ variable, e.g.:
51
+
52
+ ~~~~~ bash
53
+ export GIT_DUET_AUTHORS_FILE=$HOME/.secret-squirrel/git-authors
54
+ # ...
55
+ git duet jd am
56
+ ~~~~~
57
+
58
+ ### Workflow stuff
59
+
60
+ Set the author and committer via `git duet`:
61
+
62
+ ~~~~~ bash
63
+ git duet jd fb
64
+ ~~~~~
65
+
66
+ When you're ready to commit, use `git duet-commit` (or add an alias like
67
+ a normal person. Something like `dci = duet-commit` should work.)
68
+
69
+ ~~~~~ bash
70
+ git duet-commit -v [any other git options]
71
+ ~~~~~
72
+
73
+ When you're done pairing, set the author back to yourself with `git solo`:
74
+
75
+ ~~~~~ bash
76
+ git solo jd
77
+ ~~~~~
78
+
79
+ ### Global Config Support
80
+
81
+ If you're jumping between projects and don't want to think about
82
+ managing them all individually, you can operate on the global git
83
+ config:
84
+
85
+ ~~~~~ bash
86
+ git solo -g jd
87
+ ~~~~~
88
+
89
+ ~~~~~ bash
90
+ git duet --global jd fb
91
+ ~~~~~
92
+
93
+ ### Email Configuration
94
+
95
+ By default, email addresses are constructed from the first initial and
96
+ last name ( *or* optional username after a `;`) plus email domain, e.g.
97
+ with the following authors file:
98
+
99
+ ~~~~~ yaml
100
+ pairs:
101
+ jd: Jane Doe; jane
102
+ fb: Frances Bar
103
+ email:
104
+ domain: eternalstench.bog.local
105
+ ~~~~~
106
+
107
+ After invoking:
108
+
109
+ ~~~~~ bash
110
+ git duet jd fb
111
+ ~~~~~
112
+
113
+ Then the configured email addresses will show up like this:
114
+
115
+ ~~~~~ bash
116
+ git config user.email
117
+ # -> jane@eternalstench.bog.local
118
+ git config duet.env.git-author-email
119
+ # -> jane@eternalstench.bog.local
120
+ git config duet.env.git-committer-email
121
+ # -> f.bar@eternalstench.bog.local
122
+ ~~~~~
123
+
124
+ A custom email template may be provided via the `email_template` config
125
+ variable. The template should be a valid ERB string and the variables
126
+ available are `author` which is the full first and last name value
127
+ associated with each set of initials, `initials` which are the initials
128
+ key, and `username` which is the part following `;` in the author value.
129
+
130
+ ~~~~~ yaml
131
+ pairs:
132
+ jd: Jane Doe
133
+ fb: Frances Bar
134
+ email_template: '<%= "#{author.gsub(/ /, "-").downcase}@hamster.local" =%>'
135
+ ~~~~~
136
+
137
+ After invoking:
138
+
139
+ ~~~~~ bash
140
+ git duet jd fb
141
+ ~~~~~
142
+
143
+ Then the configured email addresses will show up like this:
144
+
145
+ ~~~~~ bash
146
+ git config user.email
147
+ # -> jane-doe@hamster.local
148
+ git config duet.env.git-author-email
149
+ # -> jane-doe@hamster.local
150
+ git config duet.env.git-committer-email
151
+ # -> frances-bar@hamster.local
152
+ ~~~~~
153
+
154
+ If there are any exceptions to either the default format or a provided
155
+ `email_template` config var, explicitly setting email addresses by
156
+ initials is supported.
157
+
158
+ ~~~~~ yaml
159
+ pairs:
160
+ jd: Jane Doe; jane
161
+ fb: Frances Bar
162
+ email:
163
+ domain: awesometown.local
164
+ email_addresses:
165
+ jd: jane@awesome.local
166
+ ~~~~~
167
+
168
+ Then Jane Doe's email will show up like this:
169
+
170
+ ~~~~~ bash
171
+ git solo jd
172
+ # ...
173
+ git config user.email
174
+ # -> jane@awesome.local
175
+ ~~~~~
176
+
177
+ Alternatively, if you have some other preferred way to look up email
178
+ addresses by initials, name or username, just use that instead:
179
+
180
+ ~~~~~ bash
181
+ export GIT_DUET_EMAIL_LOOKUP_COMMAND="$HOME/bin/custom-ldap-thingy"
182
+ # ... do work
183
+ git duet jd fb
184
+ # ... observe emails being set via the specified executable
185
+ ~~~~~
186
+
187
+ The initials, name, and username will be passed as arguments to the
188
+ lookup executable. Anything written to standard output will be used as
189
+ the email address:
190
+
191
+ ~~~~~ bash
192
+ $HOME/bin/custom-ldap-thingy 'jd' 'Jane Doe' 'jane'
193
+ # -> doej@behemoth.company.local
194
+ ~~~~~
195
+
196
+ If nothing is returned on standard output, email construction falls back
197
+ to the decisions described above.
198
+
199
+ #### Order of Precedence
200
+
201
+ Since there are multiple ways to determine an author or committer's
202
+ email, it is important to note the order of precedence used by Git Duet:
203
+
204
+ 1. Email lookup executable configured via the
205
+ `GIT_DUET_EMAIL_LOOKUP_COMMAND` environmental variable
206
+ 2. Email lookup from `email_addresses` in your configuration file
207
+ 3. Custom email address from ERB template defined in `email_template` in
208
+ your configuration file
209
+ 4. The username after the `;`, followed by `@` and the configured email
210
+ domain
211
+ 5. The lower-cased first letter of the author or committer's first name,
212
+ followed by `.` followed by the lower-cased last name of the author
213
+ or committer, followed by `@` and the configured email domain (e.g.
214
+ `f.bar@baz.local`)
215
+
216
+ ### Git hook integration
217
+
218
+ If you'd like to regularly remind yourself to set the solo or duet
219
+ initials, use `git duet-pre-commit` in your pre-commit hook:
220
+
221
+ *(in $REPO_ROOT/.git/hooks/pre-commit)*
222
+ ~~~~~ bash
223
+ #!/bin/bash
224
+ exec git duet-pre-commit
225
+ ~~~~~
226
+
227
+ The `duet-pre-commit` command will exit with a non-zero status if the
228
+ cached author and committer settings are missing or stale. The default
229
+ staleness cutoff is [20 minutes](http://en.wikipedia.org/wiki/Pomodoro_Technique),
230
+ but may be configured via the `GIT_DUET_SECONDS_AGO_STALE` environmental variable,
231
+ which should be an integer of seconds, e.g.:
232
+
233
+ ~~~~~ bash
234
+ export GIT_DUET_SECONDS_AGO_STALE=60
235
+ # ... do work for more than a minute
236
+ git commit -v
237
+ # ... pre-commit hook fires
238
+ ~~~~~
239
+
240
+ If you want to use the default hook (as shown above), install it while
241
+ in your repo like so:
242
+
243
+ ~~~~~ bash
244
+ git duet-install-hook
245
+ ~~~~~
246
+
247
+ Don't worry if you forgot you already had a `pre-commit` hook installed.
248
+ The `git duet-install-hook` command will refuse to overwrite it.
249
+
250
+ ## Compatibility
251
+
252
+ Git Duet has been tested on a bunch of platform/interpreter combinations
253
+ provided by Travis CI *not including* Rubinius.
254
+
255
+ While JRuby works it is not recommended as the VM startup time is
256
+ usually longer than it takes most Git Duet commands to execute.
257
+
258
+ If you experience badness, please [let us know via
259
+ email](mailto:github@modcloth.com) or pretty please [create an issue on
260
+ github](https://github.com/modcloth/git-duet/issues/new).
261
+
262
+ ## Contributing
263
+
264
+ 1. Fork it
265
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
266
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
267
+ 4. Push to the branch (`git push origin my-new-feature`)
268
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec) do |t|
7
+ t.rspec_opts = '--format documentation'
8
+ end
9
+
10
+ task :default => :spec
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-duet'
3
+ exit Git::Duet::Cli.run($0, ARGV)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-duet'
3
+ exit Git::Duet::Cli.run($0, ARGV)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-duet'
3
+ exit Git::Duet::Cli.run($0, ARGV)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-duet'
3
+ exit Git::Duet::Cli.run($0, ARGV)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-duet'
3
+ exit Git::Duet::Cli.run($0, ARGV)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'git-duet'
3
+ exit Git::Duet::Cli.run($0, ARGV)
@@ -0,0 +1,36 @@
1
+ # vim:fileencoding=utf-8
2
+ require File.expand_path('../lib/git/duet/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = [
6
+ 'Dan Buch',
7
+ 'Jesse Szwedko',
8
+ 'Rafe Colton',
9
+ 'Sheena McCoy',
10
+ ]
11
+ gem.email = %w(
12
+ d.buch@modcloth.com
13
+ j.szwedko@modcloth.com
14
+ r.colton@modcloth.com
15
+ sp.mccoy@modcloth.com
16
+ )
17
+ gem.description = %q{Pair programming git identity thingy}
18
+ gem.summary = "Pair harmoniously! Decide who's driving. " <<
19
+ "Commit along the way. Don't make a mess of " <<
20
+ "the repository history."
21
+ gem.homepage = ''
22
+ gem.license = 'MIT'
23
+
24
+ gem.files = `git ls-files`.split($\)
25
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
+ gem.test_files = gem.files.grep(%r{^spec/})
27
+ gem.name = 'git-duet'
28
+ gem.require_paths = %w(lib)
29
+ gem.version = Git::Duet::VERSION
30
+ gem.required_ruby_version = '>= 1.8.7'
31
+
32
+ gem.add_development_dependency 'nyan-cat-formatter', '>= 0.2.0'
33
+ gem.add_development_dependency 'rake', '>= 0.9.2.2'
34
+ gem.add_development_dependency 'rspec', '>= 2.0.0'
35
+ gem.add_development_dependency 'simplecov', '>= 0.7.0'
36
+ end
@@ -0,0 +1 @@
1
+ require 'git/duet'
@@ -0,0 +1,8 @@
1
+ module Git
2
+ module Duet
3
+ end
4
+ end
5
+
6
+ require 'git/duet/version'
7
+ require 'git/duet/cli'
8
+ require 'git/duet/key_error'
@@ -0,0 +1,77 @@
1
+ require 'yaml'
2
+ require 'erb'
3
+ require 'git/duet'
4
+
5
+ class Git::Duet::AuthorMapper
6
+ attr_accessor :authors_file
7
+
8
+ def initialize(authors_file = nil, email_lookup = nil)
9
+ @authors_file = authors_file ||
10
+ ENV['GIT_DUET_AUTHORS_FILE'] ||
11
+ File.join(ENV['HOME'], '.git-authors')
12
+ @email_lookup = email_lookup ||
13
+ ENV['GIT_DUET_EMAIL_LOOKUP_COMMAND']
14
+ end
15
+
16
+ def map(*initials_list)
17
+ author_map = {}
18
+ initials_list.each do |initials|
19
+ author_map[initials] = author_info(initials)
20
+ end
21
+ author_map
22
+ end
23
+
24
+ private
25
+ def author_info(initials)
26
+ author, username = author_map.fetch(initials).split(/;/).map(&:strip)
27
+ {
28
+ :name => author,
29
+ :email => lookup_author_email(initials, author, username)
30
+ }
31
+ end
32
+
33
+ def lookup_author_email(initials, author, username)
34
+ if @email_lookup
35
+ author_email = `#{@email_lookup} '#{initials}' '#{author}' '#{username}'`.strip
36
+ return author_email if !author_email.empty?
37
+ end
38
+
39
+ return email_addresses[initials] if email_addresses[initials]
40
+ return email_from_template(initials, author, username) if email_template
41
+ return "#{username}@#{email_domain}" if username
42
+
43
+ author_name_parts = author.split
44
+ return "#{author_name_parts.first[0,1].downcase}." <<
45
+ "#{author_name_parts.last.downcase}@#{email_domain}"
46
+ end
47
+
48
+ def email_from_template(initials, author, username)
49
+ return ERB.new(email_template).result(binding)
50
+ rescue StandardError => e
51
+ STDERR.puts("git-duet: email template rendering error: #{e.message}")
52
+ raise Git::Duet::ScriptDieError.new(8)
53
+ end
54
+
55
+ def author_map
56
+ @author_map ||= (cfg['authors'] || cfg['pairs'])
57
+ end
58
+
59
+ def email_addresses
60
+ @email_addresses ||= (cfg['email_addresses'] || {})
61
+ end
62
+
63
+ def email_domain
64
+ @email_domain ||= cfg.fetch('email').fetch('domain')
65
+ end
66
+
67
+ def email_template
68
+ @email_template || cfg['email_template']
69
+ end
70
+
71
+ def cfg
72
+ @cfg ||= YAML.load(IO.read(@authors_file))
73
+ rescue StandardError => e
74
+ STDERR.puts("git-duet: Missing or corrupt authors file: #{e.message}")
75
+ raise Git::Duet::ScriptDieError.new(3)
76
+ end
77
+ end