gitsu 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|