gitsu 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH << File.expand_path('../../../lib', __FILE__)
2
+ require 'gitsu'
@@ -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,9 @@
1
+ require 'gitsu/factory'
2
+ require 'gitsu/git'
3
+ require 'gitsu/gitsu'
4
+ require 'gitsu/runner'
5
+ require 'gitsu/shell'
6
+ require 'gitsu/switcher'
7
+ require 'gitsu/user_file'
8
+ require 'gitsu/user_list'
9
+ require 'gitsu/user'
@@ -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
@@ -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
+
@@ -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
+
@@ -0,0 +1,16 @@
1
+ module GitSu
2
+ class Shell
3
+ def capture(command)
4
+ output = `#{command}`.strip
5
+ if block_given?
6
+ yield(output, $?)
7
+ else
8
+ output
9
+ end
10
+ end
11
+
12
+ def execute(command)
13
+ system command
14
+ end
15
+ end
16
+ end
@@ -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