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
data/lib/gitsu/user.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module GitSu
|
2
|
+
class User
|
3
|
+
NONE = User.new
|
4
|
+
def NONE.to_s
|
5
|
+
"(none)"
|
6
|
+
end
|
7
|
+
def NONE.to_ansi_s(name_color, email_color, reset_color)
|
8
|
+
"#{name_color}(none)#{reset_color}"
|
9
|
+
end
|
10
|
+
|
11
|
+
class ParseError < RuntimeError
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :name, :email
|
15
|
+
|
16
|
+
def User.parse(string)
|
17
|
+
fully_qualified_user_regex = /^[^<]+<[^>]+>$/
|
18
|
+
if string =~ fully_qualified_user_regex
|
19
|
+
name = string[/^[^<]+/].strip
|
20
|
+
email = string[/<.*>/].delete "[<>]"
|
21
|
+
User.new(name, email)
|
22
|
+
else
|
23
|
+
raise ParseError, "Couldn't parse '#{string}' as user (expected user in format: 'John Smith <jsmith@example.com>')"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(name, email)
|
28
|
+
@name, @email = name, email
|
29
|
+
end
|
30
|
+
|
31
|
+
def none?
|
32
|
+
self === NONE
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
eql? other
|
37
|
+
end
|
38
|
+
|
39
|
+
def eql?(other)
|
40
|
+
@name == other.name && @email == other.email
|
41
|
+
end
|
42
|
+
|
43
|
+
def hash
|
44
|
+
to_s.hash
|
45
|
+
end
|
46
|
+
|
47
|
+
def to_ansi_s(name_color, email_color, reset_color)
|
48
|
+
"#{name_color}#{@name}#{reset_color} #{email_color}<#{@email}>#{reset_color}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
to_ansi_s("", "", "")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module GitSu
|
5
|
+
class UserFile
|
6
|
+
def initialize(file_name)
|
7
|
+
@file = file_name
|
8
|
+
unless File.exist? file_name
|
9
|
+
FileUtils.touch file_name
|
10
|
+
end
|
11
|
+
if File.size(file_name) == 0
|
12
|
+
File.open(file_name, "w") do |file|
|
13
|
+
file << "\n"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(user)
|
19
|
+
File.open(@file, "a") do |file|
|
20
|
+
file.write "\n#{user.email} : #{user.name}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def read
|
25
|
+
yaml_list = YAML.load_file(@file) or return []
|
26
|
+
yaml_list.map do |email, name|
|
27
|
+
User.new(name, email)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module GitSu
|
2
|
+
class UserList
|
3
|
+
def initialize(file_name)
|
4
|
+
@user_file = UserFile.new(file_name)
|
5
|
+
end
|
6
|
+
|
7
|
+
def add(user)
|
8
|
+
@user_file.write(user)
|
9
|
+
end
|
10
|
+
|
11
|
+
def list
|
12
|
+
@user_file.read
|
13
|
+
end
|
14
|
+
|
15
|
+
def find(search_term)
|
16
|
+
users = @user_file.read
|
17
|
+
matching_users = []
|
18
|
+
match_strategies.each do |strategy|
|
19
|
+
matching_users += users.select { |user| strategy.call(search_term, user) }
|
20
|
+
end
|
21
|
+
matching_users.first || User::NONE
|
22
|
+
end
|
23
|
+
|
24
|
+
def match_strategies
|
25
|
+
[
|
26
|
+
# Whole word of name
|
27
|
+
lambda { |search_term, user| user.name =~ /\b#{search_term}\b/i },
|
28
|
+
|
29
|
+
# Beginning of word in name
|
30
|
+
lambda { |search_term, user| user.name =~ /\b#{search_term}/i },
|
31
|
+
|
32
|
+
# Initials
|
33
|
+
lambda do |search_term, user|
|
34
|
+
initials = user.name.downcase.split(" ").map { |word| word.chars.first }.join
|
35
|
+
initials.include? search_term.downcase
|
36
|
+
end,
|
37
|
+
|
38
|
+
# Segment anywhere in name or email
|
39
|
+
lambda do |search_term, user|
|
40
|
+
name_and_email = "#{user.name} #{user.email}".downcase
|
41
|
+
name_and_email.include? search_term.downcase
|
42
|
+
end
|
43
|
+
]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
data/man/git-su.1.ronn
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
git-su(1) -- Manage your Git Users
|
2
|
+
========================================================
|
3
|
+
|
4
|
+
## SYNOPSIS
|
5
|
+
|
6
|
+
git su [OPTIONS] [user]
|
7
|
+
|
8
|
+
## DESCRIPTION
|
9
|
+
|
10
|
+
Gitsu helps you to manage your projects' Git users.
|
11
|
+
|
12
|
+
* quickly see which user Git will use to commit
|
13
|
+
* quickly switch between Git users
|
14
|
+
* configure a list of Git users to switch between
|
15
|
+
|
16
|
+
## OPTIONS
|
17
|
+
|
18
|
+
-a, --add USER
|
19
|
+
Add a user to the Gitsu users. Should be in the format "John Galt <jgalt@example.com>". You can quickly switch to one of these users by supplying a user argument to git-su.
|
20
|
+
|
21
|
+
-c, --clear
|
22
|
+
Clear all Git users. If a scope (--local, --global, or --system) is specified, only clear that scope's user.
|
23
|
+
|
24
|
+
-e, --edit
|
25
|
+
Open the Gitsu config in an editor. This is the same editor that Git is configured to use for config files.
|
26
|
+
|
27
|
+
-h, --help
|
28
|
+
Show help.
|
29
|
+
|
30
|
+
-l, --local, -g, --global, -s, --system
|
31
|
+
Specify the scope for printing, switching, or clearing selected users.
|
32
|
+
|
33
|
+
-t, --list
|
34
|
+
List the Gitsu users.
|
35
|
+
|
36
|
+
## CONFIGURATION
|
37
|
+
|
38
|
+
Gitsu supports the following configuration options, specified either in Git config files, or by executing `git config`:
|
39
|
+
|
40
|
+
git-su.defaultSelectScope
|
41
|
+
One of "local", "global", or "system" (without quotes). Specifies the default scope that the user is switched in when `git su` is run.
|
42
|
+
|
43
|
+
color.ui
|
44
|
+
Gitsu respects the value of color.ui in the same way Git does. That is, output is colored only if color.ui is set to true, and stdout is a tty.
|
45
|
+
|
46
|
+
## HOMEPAGE
|
47
|
+
|
48
|
+
http://drrb.github.com/gitsu
|
49
|
+
|
50
|
+
## EXAMPLES
|
51
|
+
|
52
|
+
Show current Git users in all scopes
|
53
|
+
|
54
|
+
`$ git su`
|
55
|
+
|
56
|
+
Show the current Git users in specified scopes
|
57
|
+
|
58
|
+
`$ git su --local --global`
|
59
|
+
|
60
|
+
Show the current Git user that Git would use to commit
|
61
|
+
|
62
|
+
`$ git whoami`
|
63
|
+
|
64
|
+
### Adding users
|
65
|
+
|
66
|
+
To add a user to Gitsu (the user will be saved in ~/.gitsu)
|
67
|
+
|
68
|
+
`$ git su --add 'Raphe Rackstraw <rrack@example.com>'`
|
69
|
+
|
70
|
+
You can also add users manually to ~/.gitsu in the following format:
|
71
|
+
|
72
|
+
jporter@example.com: Sir Joseph Porter KCB
|
73
|
+
|
74
|
+
rrack@example.com: Raphe Rackstraw
|
75
|
+
|
76
|
+
### Switching users
|
77
|
+
|
78
|
+
Switch to configured users by initials
|
79
|
+
|
80
|
+
`$ git su jp`
|
81
|
+
|
82
|
+
`Switched to Joseph Porter KCB <jporter@example.com>`
|
83
|
+
|
84
|
+
or by part of their name
|
85
|
+
|
86
|
+
`$ git su straw`
|
87
|
+
|
88
|
+
`Switched to Raphe Rackstraw <rrack@example.com>`
|
89
|
+
|
90
|
+
To clear all Git users
|
91
|
+
|
92
|
+
`$ git su --clear`
|
93
|
+
|
94
|
+
`Clearing all users from Git config`
|
95
|
+
|
96
|
+
### Scopes
|
97
|
+
|
98
|
+
Gitsu supports Git's configuration scopes: local (current repository), global (current OS user), and system (everyone on the system). As in Git, if you don't specify a scope, local scope is assumed.
|
99
|
+
|
100
|
+
To change the user for a specific scope
|
101
|
+
|
102
|
+
`$ git su joe --system`
|
103
|
+
|
104
|
+
`Switched system user to Joseph Porter KCB <jporter@example.com>`
|
105
|
+
|
106
|
+
|
107
|
+
To clear the user for a specific scope
|
108
|
+
|
109
|
+
`$ git su --clear --global`
|
110
|
+
|
111
|
+
`Clearing global user`
|
112
|
+
|
113
|
+
|
114
|
+
## AUTHOR
|
115
|
+
|
116
|
+
drrb at the git hub
|
117
|
+
|
118
|
+
## SEE ALSO
|
119
|
+
|
120
|
+
git(1), git-config(1).
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module GitSu
|
4
|
+
describe Git do
|
5
|
+
let(:shell) { double('shell') }
|
6
|
+
let(:git) { CachingGit.new(shell) }
|
7
|
+
|
8
|
+
describe '#select_user' do
|
9
|
+
it 'switches to the specified user' do
|
10
|
+
shell.should_receive(:execute).with("git config --global user.name 'John Galt'").and_return true
|
11
|
+
shell.should_receive(:execute).with("git config --global user.email 'jgalt@example.com'").and_return true
|
12
|
+
git.select_user User.new("John Galt", "jgalt@example.com"), :global
|
13
|
+
end
|
14
|
+
it 'escapes apostrophes' do
|
15
|
+
shell.should_receive(:execute).with("git config --global user.name 'John O'\\''Grady'").and_return true
|
16
|
+
shell.should_receive(:execute).with("git config --global user.email 'jo@example.com'").and_return true
|
17
|
+
git.select_user User.new("John O'Grady", "jo@example.com"), :global
|
18
|
+
end
|
19
|
+
it "raises an exception when can't set the name" do
|
20
|
+
shell.should_receive(:execute).with("git config --global user.name 'John O'\\''Grady'").and_return(false)
|
21
|
+
expect {git.select_user User.new("John O'Grady", "jo@example.com"), :global}.to raise_error(Git::ConfigSettingError)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#selected_user' do
|
26
|
+
context 'when a scope is specified' do
|
27
|
+
context 'when a user is selected' do
|
28
|
+
it 'returns the current user' do
|
29
|
+
shell.should_receive(:capture).with("git config --global user.name").and_return("John Galt")
|
30
|
+
shell.should_receive(:capture).with("git config --global user.email").and_return("jgalt@example.com")
|
31
|
+
git.selected_user(:global).should == User.new("John Galt", "jgalt@example.com")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when no user is selected' do
|
36
|
+
it 'returns the null user' do
|
37
|
+
shell.should_receive(:capture).with("git config --global user.name").and_return("")
|
38
|
+
git.selected_user(:global).should be User::NONE
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when "derived" scope is specified' do
|
44
|
+
it 'returns the current user derived by git' do
|
45
|
+
shell.should_receive(:capture).with("git config user.name").and_return("John Galt")
|
46
|
+
shell.should_receive(:capture).with("git config user.email").and_return("jgalt@example.com")
|
47
|
+
git.selected_user(:derived).should == User.new("John Galt", "jgalt@example.com")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "caches the result" do
|
52
|
+
shell.should_receive(:capture).with("git config user.name").and_return("John Galt")
|
53
|
+
shell.should_receive(:capture).with("git config user.email").and_return("jgalt@example.com")
|
54
|
+
git.selected_user(:derived).should == User.new("John Galt", "jgalt@example.com")
|
55
|
+
git.selected_user(:derived).should == User.new("John Galt", "jgalt@example.com")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#edit_gitsu_config' do
|
60
|
+
it 'opens the Gitsu config in an editor' do
|
61
|
+
shell.should_receive(:execute).with("git config --edit --file #{File.expand_path '~/.gitsu'}")
|
62
|
+
git.edit_gitsu_config
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '#color_output?' do
|
67
|
+
context 'when Git says to use colorize output' do
|
68
|
+
it 'returns true' do
|
69
|
+
shell.should_receive(:execute).with("git config --get-colorbool color.ui").and_return true
|
70
|
+
git.color_output?.should be true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when git says not to colorize output' do
|
75
|
+
it 'returns true' do
|
76
|
+
shell.should_receive(:execute).with("git config --get-colorbool color.ui").and_return false
|
77
|
+
git.color_output?.should be false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "caches the result" do
|
82
|
+
shell.should_receive(:execute).with("git config --get-colorbool color.ui").and_return false
|
83
|
+
git.color_output?.should be false
|
84
|
+
git.color_output?.should be false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#clear_user' do
|
89
|
+
context "when there's other user config" do
|
90
|
+
it 'clears the current user in the specified scope' do
|
91
|
+
shell.should_receive(:capture).with("git config --local user.name").and_return("John Smith")
|
92
|
+
shell.should_receive(:capture).with("git config --local user.email").and_return("js@example.com")
|
93
|
+
shell.should_receive(:capture).with("git config --local --unset user.name").and_return("")
|
94
|
+
shell.should_receive(:capture).with("git config --local --unset user.email").and_return("")
|
95
|
+
shell.should_receive(:capture).with("git config --local --list").and_return("ui.color=true\nuser.signingkey = something\nsomething.else")
|
96
|
+
git.clear_user(:local)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
context "when there's no other user config" do
|
100
|
+
it 'quietly attempts to remove the user section of the config in the specified scope' do
|
101
|
+
shell.should_receive(:capture).with("git config --system user.name").and_return("John Smith")
|
102
|
+
shell.should_receive(:capture).with("git config --system user.email").and_return("js@example.com")
|
103
|
+
shell.should_receive(:capture).with("git config --system --unset user.name").and_return("")
|
104
|
+
shell.should_receive(:capture).with("git config --system --unset user.email").and_return("")
|
105
|
+
shell.should_receive(:capture).with("git config --system --list").and_return("")
|
106
|
+
shell.should_receive(:capture).with("git config --system --remove-section user 2>/dev/null").and_return("")
|
107
|
+
git.clear_user(:system)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
context "when no user is selected" do
|
111
|
+
it "doesn't attempt to clear the user" do
|
112
|
+
shell.should_receive(:capture).with("git config --system user.name").and_return("")
|
113
|
+
git.clear_user :system
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe '#get_color' do
|
119
|
+
it 'gets an ANSI escape code from Git for the specified color' do
|
120
|
+
shell.should_receive(:capture).with("git config --get-color '' 'on blue'").and_return("xxx")
|
121
|
+
color = git.get_color("on blue")
|
122
|
+
color.should == "xxx"
|
123
|
+
end
|
124
|
+
it 'caches colors' do
|
125
|
+
shell.should_receive(:capture).with("git config --get-color '' 'on green'").and_return("xxx")
|
126
|
+
git.get_color("on green").should == "xxx"
|
127
|
+
git.get_color("on green").should == "xxx"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#render' do
|
132
|
+
context "when Git says to color output" do
|
133
|
+
it "colors the user output" do
|
134
|
+
user = User.new('John Galt', 'jgalt@example.com')
|
135
|
+
|
136
|
+
git.should_receive(:color_output?).and_return true
|
137
|
+
git.should_receive(:get_color).with("blue").and_return "__blue__"
|
138
|
+
git.should_receive(:get_color).with("green").and_return "__green__"
|
139
|
+
git.should_receive(:get_color).with("reset").and_return "__reset__"
|
140
|
+
|
141
|
+
git.render(user).should == "__blue__John Galt__reset__ __green__<jgalt@example.com>__reset__"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context "when Git says not to color output" do
|
146
|
+
it "calls to_s() on the user" do
|
147
|
+
user = User.new('John Galt', 'jgalt@example.com')
|
148
|
+
|
149
|
+
git.should_receive(:color_output?).and_return false
|
150
|
+
|
151
|
+
git.render(user).should == user.to_s
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe "#render_user" do
|
157
|
+
it "is equivalent to calling #render(#selected_user(scope))" do
|
158
|
+
shell.should_receive(:capture).with("git config --local user.name").and_return("John Smith")
|
159
|
+
shell.should_receive(:capture).with("git config --local user.email").and_return("js@example.com")
|
160
|
+
shell.should_receive(:execute).with("git config --get-colorbool color.ui").and_return true
|
161
|
+
shell.should_receive(:capture).with("git config --get-color '' 'blue'").and_return("*")
|
162
|
+
shell.should_receive(:capture).with("git config --get-color '' 'green'").and_return("?")
|
163
|
+
shell.should_receive(:capture).with("git config --get-color '' 'reset'").and_return("!")
|
164
|
+
git.render_user(:local).should == "*John Smith! ?<js@example.com>!"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe "#default_select_scope" do
|
169
|
+
context "when a default selecton scope is configured" do
|
170
|
+
it "returns the configured default scope" do
|
171
|
+
shell.should_receive(:capture).with("git config git-su.defaultSelectScope").and_return("global")
|
172
|
+
git.default_select_scope.should == :global
|
173
|
+
end
|
174
|
+
end
|
175
|
+
context "when no default selection scope is configured" do
|
176
|
+
it "returns the conventional default scope (local)" do
|
177
|
+
shell.should_receive(:capture).with("git config git-su.defaultSelectScope").and_return("")
|
178
|
+
git.default_select_scope.should == :local
|
179
|
+
end
|
180
|
+
end
|
181
|
+
context "when an invalid default selection scope is configured" do
|
182
|
+
it "dies noisily" do
|
183
|
+
shell.should_receive(:capture).with("git config git-su.defaultSelectScope").and_return("xxxxxx")
|
184
|
+
expect {git.default_select_scope}.to raise_error(Git::InvalidConfigError)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|