gitsu 0.0.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.
- 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
|