git-duet 0.1.1

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.
@@ -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