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