gitsu 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +21 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +676 -0
- data/README.md +38 -0
- data/Rakefile +3 -0
- data/TODO +10 -0
- data/bin/git-su +13 -0
- data/bin/git-whoami +13 -0
- data/features/add_user.feature +26 -0
- data/features/change_user_in_different_scopes.feature +24 -0
- data/features/clear_user.feature +28 -0
- data/features/configure_default_scope.feature +9 -0
- data/features/edit_config.feature +8 -0
- data/features/list_users.feature +16 -0
- data/features/print_current_user.feature +60 -0
- data/features/print_options.feature +8 -0
- data/features/step_definitions/gitsu_steps.rb +193 -0
- data/features/support/env.rb +2 -0
- data/features/switch_to_fully_qualified_user.feature +10 -0
- data/features/switch_to_stored_user.feature +49 -0
- data/gitsu.gemspec +19 -0
- data/lib/gitsu.rb +9 -0
- data/lib/gitsu/factory.rb +23 -0
- data/lib/gitsu/git.rb +136 -0
- data/lib/gitsu/gitsu.rb +94 -0
- data/lib/gitsu/runner.rb +18 -0
- data/lib/gitsu/shell.rb +16 -0
- data/lib/gitsu/switcher.rb +94 -0
- data/lib/gitsu/user.rb +55 -0
- data/lib/gitsu/user_file.rb +32 -0
- data/lib/gitsu/user_list.rb +47 -0
- data/lib/gitsu/version.rb +3 -0
- data/man/git-su.1.ronn +120 -0
- data/spec/gitsu/git_spec.rb +189 -0
- data/spec/gitsu/gitsu_spec.rb +177 -0
- data/spec/gitsu/runner_spec.rb +27 -0
- data/spec/gitsu/switcher_spec.rb +196 -0
- data/spec/gitsu/user_list_spec.rb +82 -0
- data/spec/gitsu/user_spec.rb +33 -0
- data/spec/spec_helper.rb +1 -0
- metadata +106 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
Feature: Switch to fully qualified user
|
2
|
+
As a new user
|
3
|
+
I want to switch to my Git user
|
4
|
+
So that I can commit code
|
5
|
+
|
6
|
+
Scenario: Switch to fully qualified user
|
7
|
+
Given no user is selected
|
8
|
+
When I type "git su 'John Galt <jg@example.com>'"
|
9
|
+
Then I should see "Switched local user to John Galt <jg@example.com>"
|
10
|
+
And user "John Galt <jg@example.com>" should be selected in "local" scope
|
@@ -0,0 +1,49 @@
|
|
1
|
+
Feature: Switch to stored user
|
2
|
+
As an user
|
3
|
+
I want to quickly switch to my user
|
4
|
+
So that I can commit code
|
5
|
+
|
6
|
+
Scenario Outline: One match found
|
7
|
+
Given no user is selected
|
8
|
+
And user list is
|
9
|
+
"""
|
10
|
+
rackstraw@github.com: Raphe Rackstraw
|
11
|
+
"""
|
12
|
+
When I type "git su <request>"
|
13
|
+
Then I should see "<output>"
|
14
|
+
And user "<selected_user>" should be selected in "local" scope
|
15
|
+
|
16
|
+
Scenarios: Existing user match
|
17
|
+
| request | selected_user | output |
|
18
|
+
| raphe | Raphe Rackstraw <rackstraw@github.com> | Switched local user to Raphe Rackstraw <rackstraw@github.com> |
|
19
|
+
| Raphe | Raphe Rackstraw <rackstraw@github.com> | Switched local user to Raphe Rackstraw <rackstraw@github.com> |
|
20
|
+
| git | Raphe Rackstraw <rackstraw@github.com> | Switched local user to Raphe Rackstraw <rackstraw@github.com> |
|
21
|
+
| rr | Raphe Rackstraw <rackstraw@github.com> | Switched local user to Raphe Rackstraw <rackstraw@github.com> |
|
22
|
+
| RR | Raphe Rackstraw <rackstraw@github.com> | Switched local user to Raphe Rackstraw <rackstraw@github.com> |
|
23
|
+
|
24
|
+
Scenario: No match found
|
25
|
+
Given no user is selected
|
26
|
+
And user list contains user "Raphe Rackstraw" with email "rrackstraw@github.com"
|
27
|
+
When I type "git su joe"
|
28
|
+
Then I should see "No user found matching 'joe'"
|
29
|
+
And no user should be selected
|
30
|
+
|
31
|
+
Scenario Outline: Multiple matches found
|
32
|
+
Given no user is selected
|
33
|
+
And user list is
|
34
|
+
"""
|
35
|
+
jdean@github.com: James Dean
|
36
|
+
jack@github.com: Jacky Smith
|
37
|
+
jim@github.com: Jack Smythe
|
38
|
+
amos@github.com: Amos Arphaxad
|
39
|
+
molly@github.com: Molly Meldrum
|
40
|
+
"""
|
41
|
+
When I type "git su <request>"
|
42
|
+
Then user "<selected_user>" should be selected in "local" scope
|
43
|
+
|
44
|
+
Scenarios: Existing user match
|
45
|
+
| request | selected_user | reason |
|
46
|
+
| jack | Jack Smythe <jim@github.com> | exact first name match |
|
47
|
+
| am | Amos Arphaxad <amos@github.com> | first name snippet |
|
48
|
+
| me | Molly Meldrum <molly@github.com> | other name snippet |
|
49
|
+
|
data/gitsu.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'gitsu/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "gitsu"
|
8
|
+
gem.version = GitSu::VERSION
|
9
|
+
gem.authors = ["drrb"]
|
10
|
+
gem.email = ["drrb@github.com"]
|
11
|
+
gem.description = %q{Manage your Git users}
|
12
|
+
gem.summary = %q{Gitsu allows you to quickly configure and switch between Git users}
|
13
|
+
gem.homepage = "http://drrb.github.com/gitsu"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
data/lib/gitsu.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module GitSu
|
2
|
+
class Factory
|
3
|
+
def initialize(output)
|
4
|
+
@output = output
|
5
|
+
end
|
6
|
+
|
7
|
+
def switcher
|
8
|
+
user_list_file = File.expand_path("~/.gitsu")
|
9
|
+
shell = Shell.new
|
10
|
+
git = CachingGit.new(shell)
|
11
|
+
user_list = UserList.new(user_list_file)
|
12
|
+
Switcher.new(git, user_list, @output)
|
13
|
+
end
|
14
|
+
|
15
|
+
def git_su
|
16
|
+
Gitsu.new(switcher, @output)
|
17
|
+
end
|
18
|
+
|
19
|
+
def runner
|
20
|
+
Runner.new(@output)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/gitsu/git.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
module GitSu
|
2
|
+
class Git
|
3
|
+
class InvalidConfigError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
class ConfigSettingError < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(shell)
|
10
|
+
@shell = shell
|
11
|
+
end
|
12
|
+
|
13
|
+
def config_command(scope, suffix)
|
14
|
+
command = "git config "
|
15
|
+
unless scope == :derived
|
16
|
+
command << "--#{scope} "
|
17
|
+
end
|
18
|
+
command << suffix
|
19
|
+
end
|
20
|
+
|
21
|
+
def execute_config_command(scope, command)
|
22
|
+
@shell.execute config_command(scope, command)
|
23
|
+
end
|
24
|
+
|
25
|
+
def capture_config_command(scope, command)
|
26
|
+
@shell.capture config_command(scope, command)
|
27
|
+
end
|
28
|
+
|
29
|
+
def get_config(scope, key)
|
30
|
+
capture_config_command(scope, key)
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_config(scope, key, value)
|
34
|
+
# replace <'> with <'\''>. E.g. O'Grady -> O'\''Grady
|
35
|
+
escaped_value = value.gsub(/'/, "'\\\\\''")
|
36
|
+
execute_config_command(scope, "#{key} '#{escaped_value}'")
|
37
|
+
end
|
38
|
+
|
39
|
+
def unset_config(scope, key)
|
40
|
+
capture_config_command(scope, "--unset #{key}")
|
41
|
+
end
|
42
|
+
|
43
|
+
def list_config(scope)
|
44
|
+
capture_config_command(scope, "--list").chomp.split("\n")
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_config_section(scope, section)
|
48
|
+
capture_config_command(scope, "--remove-section #{section} 2>/dev/null")
|
49
|
+
end
|
50
|
+
|
51
|
+
def get_color(color_name)
|
52
|
+
capture_config_command(:derived, "--get-color '' '#{color_name}'")
|
53
|
+
end
|
54
|
+
|
55
|
+
def select_user(user, scope)
|
56
|
+
set_config(scope, "user.name", user.name) or raise ConfigSettingError, "Couldn't update user config in '#{scope}' scope"
|
57
|
+
set_config(scope, "user.email", user.email)
|
58
|
+
end
|
59
|
+
|
60
|
+
def selected_user(scope)
|
61
|
+
name = get_config(scope, "user.name")
|
62
|
+
if name.empty?
|
63
|
+
User::NONE
|
64
|
+
else
|
65
|
+
email = get_config(scope, "user.email")
|
66
|
+
User.new(name, email)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def edit_gitsu_config
|
71
|
+
execute_config_command(:derived, "--edit --file #{File.expand_path '~/.gitsu'}")
|
72
|
+
end
|
73
|
+
|
74
|
+
def clear_user(scope)
|
75
|
+
unset_config(scope, "user.name")
|
76
|
+
unset_config(scope, "user.email")
|
77
|
+
if list_config(scope).select { |e| e =~ /^user\./ }.empty?
|
78
|
+
remove_config_section(scope, "user")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def color_output?
|
83
|
+
execute_config_command(:derived, "--get-colorbool color.ui")
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_select_scope
|
87
|
+
scope_string = get_config(:derived, "git-su.defaultSelectScope")
|
88
|
+
if scope_string.empty?
|
89
|
+
:local
|
90
|
+
elsif scope_string =~ /^(local|global|system)$/
|
91
|
+
scope_string.to_sym
|
92
|
+
else
|
93
|
+
raise InvalidConfigError, "Invalid configuration value found for git-su.defaultSelectScope: '#{scope_string}'. Expected one of 'local', 'global', or 'system'."
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def render(user)
|
98
|
+
if color_output?
|
99
|
+
user_color = get_color "blue"
|
100
|
+
email_color = get_color "green"
|
101
|
+
reset_color = get_color "reset"
|
102
|
+
user.to_ansi_s(user_color, email_color, reset_color)
|
103
|
+
else
|
104
|
+
user.to_s
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def render_user(scope)
|
109
|
+
render selected_user(scope)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
class CachingGit < Git
|
115
|
+
|
116
|
+
def get_color(color_name)
|
117
|
+
@colors ||= {}
|
118
|
+
#TODO: what if it's an invalid color?
|
119
|
+
@colors[color_name] ||= super
|
120
|
+
end
|
121
|
+
|
122
|
+
def color_output?
|
123
|
+
@color_output.nil? ? @color_output = super : @color_output
|
124
|
+
end
|
125
|
+
|
126
|
+
def clear_user(scope)
|
127
|
+
# Git complains if you try to clear the user when the config file is missing
|
128
|
+
super unless selected_user(scope).none?
|
129
|
+
end
|
130
|
+
|
131
|
+
def selected_user(scope)
|
132
|
+
@selected_users ||= {}
|
133
|
+
@selected_users[scope] ||= super
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/gitsu/gitsu.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module GitSu
|
4
|
+
class Gitsu
|
5
|
+
|
6
|
+
def initialize(switcher, output)
|
7
|
+
@switcher, @output = switcher, output
|
8
|
+
end
|
9
|
+
|
10
|
+
def go(args)
|
11
|
+
options = {}
|
12
|
+
optparse = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: git-su [options] user"
|
14
|
+
|
15
|
+
opts.on('-t', '--list', 'List the configured users') do
|
16
|
+
options[:list] = true
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on('-c', '--clear', 'Clear the current user') do
|
20
|
+
options[:clear] = true
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('-a', '--add USER <EMAIL>', 'Add a user in email format (e.g. John Citizen <jcitizen@example.com>)') do |user|
|
24
|
+
options[:add] = user
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on('-e', '--edit', 'Open the Gitsu config file in an editor') do
|
28
|
+
options[:edit] = true
|
29
|
+
end
|
30
|
+
|
31
|
+
options[:scope] = []
|
32
|
+
opts.on('-l', '--local', 'Change user in local scope') do
|
33
|
+
options[:scope] << :local
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on('-g', '--global', 'Change user in global scope') do
|
37
|
+
options[:scope] << :global
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on('-s', '--system', 'Change user in system scope') do
|
41
|
+
options[:scope] << :system
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on('-h', '--help', 'Show this message') do
|
45
|
+
options[:help] = true
|
46
|
+
@output.puts opts
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
optparse.parse! args
|
51
|
+
run(options, args)
|
52
|
+
end
|
53
|
+
|
54
|
+
def run(options, args)
|
55
|
+
if options[:help]
|
56
|
+
return
|
57
|
+
end
|
58
|
+
|
59
|
+
if options[:list]
|
60
|
+
@switcher.list
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
64
|
+
if options[:edit]
|
65
|
+
@switcher.edit_config
|
66
|
+
return
|
67
|
+
end
|
68
|
+
|
69
|
+
scopes = options[:scope]
|
70
|
+
|
71
|
+
if options[:clear]
|
72
|
+
clear_scopes = scopes.empty? ? [:all] : scopes
|
73
|
+
@switcher.clear *clear_scopes
|
74
|
+
end
|
75
|
+
|
76
|
+
if options[:add]
|
77
|
+
@switcher.add options[:add]
|
78
|
+
end
|
79
|
+
|
80
|
+
if args.empty?
|
81
|
+
unless options[:add] || options [:clear]
|
82
|
+
print_scopes = scopes.empty? ? [:all] : scopes
|
83
|
+
@switcher.print_current *print_scopes
|
84
|
+
end
|
85
|
+
else
|
86
|
+
select_scopes = scopes.empty? ? [:default] : scopes
|
87
|
+
select_scopes.each do |scope|
|
88
|
+
@switcher.request(args.join(" "), scope)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
data/lib/gitsu/runner.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module GitSu
|
2
|
+
class Runner
|
3
|
+
def initialize(output)
|
4
|
+
@output = output
|
5
|
+
end
|
6
|
+
|
7
|
+
def run
|
8
|
+
begin
|
9
|
+
yield
|
10
|
+
rescue Interrupt => interrupted
|
11
|
+
@output.puts "Interrupted"
|
12
|
+
rescue StandardError => error
|
13
|
+
@output.puts error.message
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
data/lib/gitsu/shell.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
class Array
|
2
|
+
def list
|
3
|
+
if empty?
|
4
|
+
""
|
5
|
+
elsif size == 1
|
6
|
+
last.to_s
|
7
|
+
else
|
8
|
+
"#{self[0..-2].map{|e| e.to_s}.join(", ")} and #{last.to_s}"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def pluralize(word)
|
13
|
+
size > 1 ? word + "s" : word
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module GitSu
|
18
|
+
|
19
|
+
class Switcher
|
20
|
+
def initialize(git, user_list, output)
|
21
|
+
@git, @user_list, @output = git, user_list, output
|
22
|
+
end
|
23
|
+
|
24
|
+
def request(user, scope)
|
25
|
+
begin
|
26
|
+
matching_user = User.parse(user)
|
27
|
+
rescue User::ParseError => parse_error
|
28
|
+
matching_user = @user_list.find(user)
|
29
|
+
end
|
30
|
+
|
31
|
+
if matching_user.none?
|
32
|
+
@output.puts "No user found matching '#{user}'"
|
33
|
+
else
|
34
|
+
scope = scope == :default ? @git.default_select_scope : scope
|
35
|
+
@git.select_user(matching_user, scope)
|
36
|
+
@output.puts "Switched #{scope} user to #{@git.render matching_user}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_current(*scopes)
|
41
|
+
if scopes.include? :all
|
42
|
+
@output.puts "Current user: #{@git.render_user(:derived)}"
|
43
|
+
@output.puts
|
44
|
+
@output.puts "Local: #{@git.render_user(:local)}"
|
45
|
+
@output.puts "Global: #{@git.render_user(:global)}"
|
46
|
+
@output.puts "System: #{@git.render_user(:system)}"
|
47
|
+
else
|
48
|
+
scopes.each do |scope|
|
49
|
+
unless @git.selected_user(scope).none?
|
50
|
+
@output.puts @git.render_user(scope)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def edit_config
|
57
|
+
@git.edit_gitsu_config
|
58
|
+
end
|
59
|
+
|
60
|
+
def clear(*scopes)
|
61
|
+
scope_list = scopes.list
|
62
|
+
|
63
|
+
if scopes.include? :all
|
64
|
+
scopes = [:local, :global, :system]
|
65
|
+
end
|
66
|
+
|
67
|
+
@output.puts "Clearing Git #{scopes.pluralize('user')} in #{scope_list} #{scopes.pluralize('scope')}"
|
68
|
+
scopes.each do |scope|
|
69
|
+
@git.clear_user(scope)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def list
|
74
|
+
@user_list.list.each do |user|
|
75
|
+
@output.puts @git.render(user)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def add(user_string)
|
80
|
+
begin
|
81
|
+
user = User.parse(user_string)
|
82
|
+
rescue User::ParseError => parse_error
|
83
|
+
@output.puts parse_error.message
|
84
|
+
return
|
85
|
+
end
|
86
|
+
if @user_list.list.include? user
|
87
|
+
@output.puts "User '#{user}' already in user list"
|
88
|
+
else
|
89
|
+
@user_list.add user
|
90
|
+
@output.puts "User '#{user}' added to users"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|