gitsu 1.0.0 → 2.0.0
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.
- checksums.yaml +7 -0
- data/.licenseignore +2 -0
- data/.simplecov +16 -0
- data/.travis.yml +19 -2
- data/Gemfile +17 -1
- data/README.md +36 -7
- data/Rakefile +37 -3
- data/TODO +4 -2
- data/bin/git-su +15 -0
- data/bin/git-whoami +15 -0
- data/features/add_user.feature +16 -0
- data/features/change_user_in_different_scopes.feature +16 -0
- data/features/clear_user.feature +16 -0
- data/features/configure_default_scope.feature +22 -2
- data/features/edit_config.feature +16 -0
- data/features/list_users.feature +16 -0
- data/features/print_current_user.feature +16 -0
- data/features/print_options.feature +16 -0
- data/features/step_definitions/gitsu_steps.rb +33 -12
- data/features/support/env.rb +16 -0
- data/features/switch_to_fully_qualified_user.feature +21 -0
- data/features/switch_to_multiple_users.feature +64 -0
- data/features/switch_to_stored_user.feature +16 -0
- data/gitsu.gemspec +20 -3
- data/lib/gitsu.rb +17 -0
- data/lib/gitsu/array.rb +31 -0
- data/lib/gitsu/config_repository.rb +49 -0
- data/lib/gitsu/factory.rb +26 -3
- data/lib/gitsu/git.rb +44 -35
- data/lib/gitsu/gitsu.rb +27 -3
- data/lib/gitsu/runner.rb +16 -0
- data/lib/gitsu/shell.rb +17 -2
- data/lib/gitsu/switcher.rb +68 -30
- data/lib/gitsu/user.rb +80 -11
- data/lib/gitsu/user_file.rb +18 -2
- data/lib/gitsu/user_list.rb +68 -12
- data/lib/gitsu/version.rb +17 -1
- data/man/git-su.1.ronn +36 -4
- data/spec/gitsu/array_spec.rb +71 -0
- data/spec/gitsu/bin_spec.rb +16 -0
- data/spec/gitsu/config_repository_spec.rb +67 -0
- data/spec/gitsu/git_spec.rb +36 -15
- data/spec/gitsu/gitsu_spec.rb +41 -18
- data/spec/gitsu/runner_spec.rb +16 -0
- data/spec/gitsu/shell_spec.rb +55 -0
- data/spec/gitsu/switcher_spec.rb +53 -13
- data/spec/gitsu/user_file_spec.rb +91 -0
- data/spec/gitsu/user_list_spec.rb +112 -41
- data/spec/gitsu/user_spec.rb +97 -0
- data/spec/gitsu/version_spec.rb +16 -0
- data/spec/spec_helper.rb +16 -0
- metadata +45 -28
data/lib/gitsu/gitsu.rb
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
1
17
|
require 'optparse'
|
2
18
|
|
3
19
|
module GitSu
|
@@ -10,7 +26,14 @@ module GitSu
|
|
10
26
|
def go(args)
|
11
27
|
options = {}
|
12
28
|
optparse = OptionParser.new do |opts|
|
13
|
-
opts.banner =
|
29
|
+
opts.banner = <<-BANNER
|
30
|
+
Gitsu Copyright (C) 2013 drrb
|
31
|
+
This program comes with ABSOLUTELY NO WARRANTY.
|
32
|
+
This is free software, and you are welcome to redistribute it under
|
33
|
+
certain conditions; see <http://www.gnu.org/licenses/> for details.
|
34
|
+
|
35
|
+
Usage: git-su [options] user
|
36
|
+
BANNER
|
14
37
|
|
15
38
|
opts.on('-t', '--list', 'List the configured users') do
|
16
39
|
options[:list] = true
|
@@ -20,7 +43,7 @@ module GitSu
|
|
20
43
|
options[:clear] = true
|
21
44
|
end
|
22
45
|
|
23
|
-
opts.on('-a', '--add USER
|
46
|
+
opts.on('-a', '--add USER', 'Add a user in email format (e.g. John Citizen <jcitizen@example.com>)') do |user|
|
24
47
|
options[:add] = user
|
25
48
|
end
|
26
49
|
|
@@ -51,6 +74,7 @@ module GitSu
|
|
51
74
|
run(options, args)
|
52
75
|
end
|
53
76
|
|
77
|
+
private
|
54
78
|
def run(options, args)
|
55
79
|
if options[:help]
|
56
80
|
return
|
@@ -85,7 +109,7 @@ module GitSu
|
|
85
109
|
else
|
86
110
|
select_scopes = scopes.empty? ? [:default] : scopes
|
87
111
|
select_scopes.each do |scope|
|
88
|
-
@switcher.request(
|
112
|
+
@switcher.request(scope, *args)
|
89
113
|
end
|
90
114
|
end
|
91
115
|
end
|
data/lib/gitsu/runner.rb
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
1
17
|
module GitSu
|
2
18
|
class Runner
|
3
19
|
def initialize(output)
|
data/lib/gitsu/shell.rb
CHANGED
@@ -1,12 +1,27 @@
|
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
1
17
|
module GitSu
|
2
18
|
class Shell
|
3
19
|
def capture(command)
|
4
20
|
output = `#{command}`.strip
|
5
21
|
if block_given?
|
6
22
|
yield(output, $?)
|
7
|
-
else
|
8
|
-
output
|
9
23
|
end
|
24
|
+
output
|
10
25
|
end
|
11
26
|
|
12
27
|
def execute(command)
|
data/lib/gitsu/switcher.rb
CHANGED
@@ -1,39 +1,36 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
11
16
|
|
12
|
-
|
13
|
-
size > 1 ? word + "s" : word
|
14
|
-
end
|
15
|
-
end
|
17
|
+
require 'gitsu/array'
|
16
18
|
|
17
19
|
module GitSu
|
18
|
-
|
19
20
|
class Switcher
|
20
|
-
def initialize(git, user_list, output)
|
21
|
-
@git, @user_list, @output = git, user_list, output
|
21
|
+
def initialize(config_repository, git, user_list, output)
|
22
|
+
@config_repository, @git, @user_list, @output = config_repository, git, user_list, output
|
22
23
|
end
|
23
24
|
|
24
|
-
def request(
|
25
|
+
def request(scope, *user_strings)
|
25
26
|
begin
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@output.puts
|
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}"
|
27
|
+
parsed, not_parsed = try_to_parse_all user_strings
|
28
|
+
found = find_all not_parsed
|
29
|
+
combined_user = combine_all(parsed + found)
|
30
|
+
select_user combined_user, scope
|
31
|
+
parsed.each {|user| add_parsed_user(user) }
|
32
|
+
rescue RuntimeError => error
|
33
|
+
@output.puts error.message
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
@@ -58,7 +55,7 @@ module GitSu
|
|
58
55
|
end
|
59
56
|
|
60
57
|
def clear(*scopes)
|
61
|
-
scope_list = scopes.
|
58
|
+
scope_list = scopes.to_sentence
|
62
59
|
|
63
60
|
if scopes.include? :all
|
64
61
|
scopes = [:local, :global, :system]
|
@@ -83,6 +80,24 @@ module GitSu
|
|
83
80
|
@output.puts parse_error.message
|
84
81
|
return
|
85
82
|
end
|
83
|
+
add_parsed_user user
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
def try_to_parse_all(user_strings)
|
88
|
+
parsed = []
|
89
|
+
not_parsed = []
|
90
|
+
user_strings.each do |user_string|
|
91
|
+
begin
|
92
|
+
parsed << User.parse(user_string)
|
93
|
+
rescue User::ParseError => parse_error
|
94
|
+
not_parsed << user_string
|
95
|
+
end
|
96
|
+
end
|
97
|
+
return [parsed, not_parsed]
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_parsed_user(user)
|
86
101
|
if @user_list.list.include? user
|
87
102
|
@output.puts "User '#{user}' already in user list"
|
88
103
|
else
|
@@ -90,5 +105,28 @@ module GitSu
|
|
90
105
|
@output.puts "User '#{user}' added to users"
|
91
106
|
end
|
92
107
|
end
|
108
|
+
|
109
|
+
def find_all(user_strings)
|
110
|
+
if user_strings.empty?
|
111
|
+
[]
|
112
|
+
else
|
113
|
+
@user_list.find *user_strings
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def combine_all(users)
|
118
|
+
group_email = @config_repository.group_email_address
|
119
|
+
users.inject(User::NONE) do |combined_user, user|
|
120
|
+
combined_user.combine user, group_email
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def select_user(user, scope)
|
125
|
+
if scope == :default
|
126
|
+
scope = @config_repository.default_select_scope
|
127
|
+
end
|
128
|
+
@git.select_user(user, scope)
|
129
|
+
@output.puts "Switched #{scope} user to #{@git.render user}"
|
130
|
+
end
|
93
131
|
end
|
94
132
|
end
|
data/lib/gitsu/user.rb
CHANGED
@@ -1,18 +1,41 @@
|
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
17
|
+
require 'gitsu/array'
|
18
|
+
|
1
19
|
module GitSu
|
2
20
|
class User
|
3
|
-
|
21
|
+
class ParseError < RuntimeError
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :names, :emails
|
25
|
+
protected :names, :emails
|
26
|
+
|
27
|
+
def initialize(name, email)
|
28
|
+
@names, @emails = [name], [email]
|
29
|
+
end
|
30
|
+
|
31
|
+
NONE = User.new(nil, nil)
|
4
32
|
def NONE.to_s
|
5
33
|
"(none)"
|
6
34
|
end
|
7
35
|
def NONE.to_ansi_s(name_color, email_color, reset_color)
|
8
|
-
|
36
|
+
name_color + to_s + reset_color
|
9
37
|
end
|
10
38
|
|
11
|
-
class ParseError < RuntimeError
|
12
|
-
end
|
13
|
-
|
14
|
-
attr_accessor :name, :email
|
15
|
-
|
16
39
|
def User.parse(string)
|
17
40
|
fully_qualified_user_regex = /^[^<]+<[^>]+>$/
|
18
41
|
if string =~ fully_qualified_user_regex
|
@@ -24,8 +47,39 @@ module GitSu
|
|
24
47
|
end
|
25
48
|
end
|
26
49
|
|
27
|
-
def
|
28
|
-
|
50
|
+
def combine(other, group_email)
|
51
|
+
if none?
|
52
|
+
other
|
53
|
+
elsif other.none?
|
54
|
+
self
|
55
|
+
else
|
56
|
+
clone.combine! other, group_email
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def clone
|
61
|
+
deep_clone = super
|
62
|
+
deep_clone.names = names.clone
|
63
|
+
deep_clone.emails = emails.clone
|
64
|
+
deep_clone
|
65
|
+
end
|
66
|
+
|
67
|
+
def name
|
68
|
+
names = emails_and_names.map {|email,name| name}
|
69
|
+
names.to_sentence
|
70
|
+
end
|
71
|
+
|
72
|
+
def email
|
73
|
+
emails = emails_and_names.map {|email,name| email}
|
74
|
+
if emails.size == 1
|
75
|
+
emails.first
|
76
|
+
else
|
77
|
+
email_prefixes = emails.map { |email| email.sub /@.*/, '' }
|
78
|
+
email_domain = emails.first.sub /^.*@/, ''
|
79
|
+
group_email_prefix = @group_email.sub /@.*/, ''
|
80
|
+
group_email_domain = @group_email.sub /^.*@/, ''
|
81
|
+
group_email_prefix + '+' + email_prefixes.join('+') + '@' + group_email_domain
|
82
|
+
end
|
29
83
|
end
|
30
84
|
|
31
85
|
def none?
|
@@ -37,7 +91,7 @@ module GitSu
|
|
37
91
|
end
|
38
92
|
|
39
93
|
def eql?(other)
|
40
|
-
|
94
|
+
name == other.name && email == other.email
|
41
95
|
end
|
42
96
|
|
43
97
|
def hash
|
@@ -45,11 +99,26 @@ module GitSu
|
|
45
99
|
end
|
46
100
|
|
47
101
|
def to_ansi_s(name_color, email_color, reset_color)
|
48
|
-
"#{name_color}#{
|
102
|
+
"#{name_color}#{name}#{reset_color} #{email_color}<#{email}>#{reset_color}"
|
49
103
|
end
|
50
104
|
|
51
105
|
def to_s
|
52
106
|
to_ansi_s("", "", "")
|
53
107
|
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
def combine!(other, group_email)
|
111
|
+
@names += other.names
|
112
|
+
@emails += other.emails
|
113
|
+
@group_email = group_email
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
# Array of emails and names, unique and ordered
|
119
|
+
def emails_and_names
|
120
|
+
combined = @emails.zip @names
|
121
|
+
Hash[combined].sort
|
122
|
+
end
|
54
123
|
end
|
55
124
|
end
|
data/lib/gitsu/user_file.rb
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
1
17
|
require 'fileutils'
|
2
18
|
require 'yaml'
|
3
19
|
|
@@ -22,8 +38,8 @@ module GitSu
|
|
22
38
|
end
|
23
39
|
|
24
40
|
def read
|
25
|
-
|
26
|
-
|
41
|
+
user_map = YAML.load_file(@file) or return []
|
42
|
+
user_map.map do |email, name|
|
27
43
|
User.new(name, email)
|
28
44
|
end
|
29
45
|
end
|
data/lib/gitsu/user_list.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
|
+
# Gitsu
|
2
|
+
# Copyright (C) 2013 drrb
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
|
1
17
|
module GitSu
|
2
18
|
class UserList
|
3
|
-
def initialize(
|
4
|
-
@user_file =
|
19
|
+
def initialize(user_file)
|
20
|
+
@user_file = user_file
|
5
21
|
end
|
6
22
|
|
7
23
|
def add(user)
|
@@ -12,13 +28,54 @@ module GitSu
|
|
12
28
|
@user_file.read
|
13
29
|
end
|
14
30
|
|
15
|
-
def find(
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
31
|
+
def find(*search_terms)
|
32
|
+
all_users = list
|
33
|
+
searches = all_matching_users(all_users, search_terms)
|
34
|
+
searches.each do |search|
|
35
|
+
if search.matches.empty?
|
36
|
+
raise "No user found matching '#{search.term}'"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
find_unique_combination(searches) or raise "Couldn't find a combination of unique users matching #{search_terms.map {|t| "'#{t}'" }.to_sentence}"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
class Search
|
45
|
+
attr_accessor :term, :matches
|
46
|
+
def initialize(term)
|
47
|
+
@term, @matches = term, []
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def all_matching_users(all_users, search_terms)
|
52
|
+
searches = search_terms.map {|search_term| Search.new(search_term)}
|
53
|
+
searches.each do |search|
|
54
|
+
match_strategies.each do |strategy|
|
55
|
+
search.matches += all_users.select { |user| strategy.call(search.term, user) }
|
56
|
+
end
|
57
|
+
search.matches.uniq!
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def find_unique_combination(searches)
|
62
|
+
searches = searches.clone
|
63
|
+
# Generate all combinations
|
64
|
+
combinations = [[]]
|
65
|
+
searches.each do |search|
|
66
|
+
previous_combos = combinations.clone
|
67
|
+
combinations = []
|
68
|
+
previous_combos.each do |combo|
|
69
|
+
search.matches.each do |match|
|
70
|
+
combo_extension = combo.clone
|
71
|
+
combo_extension << match
|
72
|
+
combinations << combo_extension
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
combinations.find do |combo|
|
77
|
+
combo.uniq.size == combo.size
|
20
78
|
end
|
21
|
-
matching_users.first || User::NONE
|
22
79
|
end
|
23
80
|
|
24
81
|
def match_strategies
|
@@ -31,14 +88,13 @@ module GitSu
|
|
31
88
|
|
32
89
|
# Initials
|
33
90
|
lambda do |search_term, user|
|
34
|
-
initials = user.name.
|
35
|
-
initials
|
91
|
+
initials = user.name.split(" ").map { |word| word.chars.first }.join
|
92
|
+
initials =~ /#{search_term}/i
|
36
93
|
end,
|
37
94
|
|
38
95
|
# Segment anywhere in name or email
|
39
96
|
lambda do |search_term, user|
|
40
|
-
|
41
|
-
name_and_email.include? search_term.downcase
|
97
|
+
"#{user.name} #{user.email}" =~ /#{search_term}/i
|
42
98
|
end
|
43
99
|
]
|
44
100
|
end
|