worktree_manager 0.2.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.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +52 -0
- data/.gitignore +37 -0
- data/.mise.toml +9 -0
- data/.rspec +3 -0
- data/.version +1 -0
- data/.worktree.yml.example +45 -0
- data/CHANGELOG.md +50 -0
- data/CLAUDE.md +17 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +53 -0
- data/LICENSE +9 -0
- data/README.md +391 -0
- data/Rakefile +6 -0
- data/bin/wm +6 -0
- data/bin/worktree_manager +6 -0
- data/lib/worktree_manager/cli.rb +794 -0
- data/lib/worktree_manager/config_manager.rb +64 -0
- data/lib/worktree_manager/hook_manager.rb +269 -0
- data/lib/worktree_manager/manager.rb +126 -0
- data/lib/worktree_manager/version.rb +3 -0
- data/lib/worktree_manager/worktree.rb +56 -0
- data/lib/worktree_manager.rb +11 -0
- data/mise/release.sh +120 -0
- data/pkg/worktree_manager-0.1.0.gem +0 -0
- data/spec/integration/worktree_integration_spec.rb +288 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/worktree_manager/cli_help_spec.rb +154 -0
- data/spec/worktree_manager/cli_spec.rb +976 -0
- data/spec/worktree_manager/config_manager_spec.rb +172 -0
- data/spec/worktree_manager/hook_manager_spec.rb +581 -0
- data/spec/worktree_manager/manager_spec.rb +95 -0
- data/spec/worktree_manager/worktree_spec.rb +83 -0
- data/spec/worktree_manager_spec.rb +14 -0
- data/worktree_manager.gemspec +33 -0
- metadata +136 -0
@@ -0,0 +1,288 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tmpdir'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
RSpec.describe 'Worktree Integration Tests' do
|
7
|
+
let(:original_dir) { Dir.pwd }
|
8
|
+
let(:test_id) { Time.now.to_i }
|
9
|
+
let(:test_dir) { Dir.mktmpdir("worktree_integration_#{test_id}") }
|
10
|
+
let(:main_repo_path) { File.join(test_dir, 'main-repo') }
|
11
|
+
let(:worktree_path) { File.join(test_dir, 'test-worktree') }
|
12
|
+
let(:branch_name) { "test/branch-#{test_id}" }
|
13
|
+
let(:hook_file) { File.join(main_repo_path, '.worktree.yml') }
|
14
|
+
|
15
|
+
around do |example|
|
16
|
+
# Save original directory
|
17
|
+
Dir.chdir(original_dir) do
|
18
|
+
# Create test Git repository
|
19
|
+
Dir.chdir(test_dir) do
|
20
|
+
`git init main-repo`
|
21
|
+
Dir.chdir('main-repo') do
|
22
|
+
`git config user.name "Test User"`
|
23
|
+
`git config user.email "test@example.com"`
|
24
|
+
File.write('README.md', '# Test Repository')
|
25
|
+
`git add README.md`
|
26
|
+
`git commit -m "Initial commit"`
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create hook file
|
31
|
+
hook_config = <<~YAML
|
32
|
+
hooks:
|
33
|
+
pre_add:
|
34
|
+
commands:
|
35
|
+
- "echo \\"Test Hook: Starting worktree creation $WORKTREE_PATH\\""
|
36
|
+
post_add:
|
37
|
+
commands:
|
38
|
+
- "echo \\"Test Hook: Worktree creation completed $WORKTREE_PATH\\""
|
39
|
+
- "echo \\"Branch: $WORKTREE_BRANCH\\""
|
40
|
+
pre_remove:
|
41
|
+
commands:
|
42
|
+
- "echo \\"Test Hook: Starting worktree removal $WORKTREE_PATH\\""
|
43
|
+
post_remove:
|
44
|
+
commands:
|
45
|
+
- "echo \\"Test Hook: Worktree removal completed $WORKTREE_PATH\\""
|
46
|
+
YAML
|
47
|
+
File.write(hook_file, hook_config)
|
48
|
+
|
49
|
+
# Run example in main repository
|
50
|
+
Dir.chdir(main_repo_path) do
|
51
|
+
example.run
|
52
|
+
end
|
53
|
+
end
|
54
|
+
ensure
|
55
|
+
# Clean up test directory
|
56
|
+
FileUtils.rm_rf(test_dir) if Dir.exist?(test_dir)
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'Complete workflow tests' do
|
60
|
+
it 'works correctly from worktree creation to removal' do
|
61
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
62
|
+
hook_manager = WorktreeManager::HookManager.new(main_repo_path)
|
63
|
+
|
64
|
+
# 1. Check initial state (main repository is also included as a worktree)
|
65
|
+
initial_worktrees = manager.list
|
66
|
+
initial_count = initial_worktrees.size
|
67
|
+
|
68
|
+
# 2. Create worktree with new branch
|
69
|
+
expect(hook_manager.execute_hook(:pre_add, path: worktree_path, branch: branch_name)).to be true
|
70
|
+
|
71
|
+
worktree = manager.add_with_new_branch(worktree_path, branch_name)
|
72
|
+
expect(worktree).not_to be_nil
|
73
|
+
expect(worktree.path).to eq(worktree_path)
|
74
|
+
expect(worktree.branch).to eq(branch_name)
|
75
|
+
|
76
|
+
expect(hook_manager.execute_hook(:post_add, path: worktree_path, branch: branch_name)).to be true
|
77
|
+
|
78
|
+
# 3. Verify created worktree
|
79
|
+
worktrees = manager.list
|
80
|
+
expect(worktrees.size).to eq(initial_count + 1)
|
81
|
+
|
82
|
+
new_worktree = worktrees.find do |w|
|
83
|
+
File.realpath(w.path) == File.realpath(worktree_path)
|
84
|
+
end
|
85
|
+
expect(new_worktree).not_to be_nil
|
86
|
+
expect(new_worktree.branch).to eq("refs/heads/#{branch_name}")
|
87
|
+
|
88
|
+
# 4. Verify worktree directory in file system
|
89
|
+
expect(Dir.exist?(worktree_path)).to be true
|
90
|
+
expect(File.exist?(File.join(worktree_path, '.git'))).to be true
|
91
|
+
|
92
|
+
# 5. Remove worktree with hooks
|
93
|
+
expect(hook_manager.execute_hook(:pre_remove, path: worktree_path, branch: branch_name)).to be true
|
94
|
+
|
95
|
+
expect(manager.remove(worktree_path)).to be true
|
96
|
+
|
97
|
+
expect(hook_manager.execute_hook(:post_remove, path: worktree_path, branch: branch_name)).to be true
|
98
|
+
|
99
|
+
# 6. Verify removal
|
100
|
+
expect(manager.list.size).to eq(initial_count)
|
101
|
+
expect(Dir.exist?(worktree_path)).to be false
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'raises error when creating multiple times at the same path' do
|
105
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
106
|
+
|
107
|
+
# First creation
|
108
|
+
worktree1 = manager.add_with_new_branch(worktree_path, branch_name)
|
109
|
+
expect(worktree1).not_to be_nil
|
110
|
+
|
111
|
+
# Try to create again at the same path
|
112
|
+
expect do
|
113
|
+
manager.add_with_new_branch(worktree_path, "#{branch_name}-2")
|
114
|
+
end.to raise_error(WorktreeManager::Error, /already exists/)
|
115
|
+
|
116
|
+
# Cleanup
|
117
|
+
manager.remove(worktree_path)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'raises error when removing non-existent worktree' do
|
121
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
122
|
+
|
123
|
+
expect do
|
124
|
+
manager.remove('/non/existent/path')
|
125
|
+
end.to raise_error(WorktreeManager::Error, /is not a working tree/)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe 'Hook system integration tests' do
|
130
|
+
it 'handles errors during hook execution properly' do
|
131
|
+
# Set up hook that causes error
|
132
|
+
error_hook_file = File.join(main_repo_path, '.worktree.yml')
|
133
|
+
error_hook_config = <<~YAML
|
134
|
+
hooks:
|
135
|
+
pre_add:
|
136
|
+
commands:
|
137
|
+
- "exit 1"
|
138
|
+
stop_on_error: true
|
139
|
+
YAML
|
140
|
+
File.write(error_hook_file, error_hook_config)
|
141
|
+
|
142
|
+
hook_manager = WorktreeManager::HookManager.new(main_repo_path)
|
143
|
+
|
144
|
+
# Hook execution returns false
|
145
|
+
expect(hook_manager.execute_hook(:pre_add, path: worktree_path, branch: branch_name)).to be false
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'passes environment variables correctly to hooks' do
|
149
|
+
# Set up hook that outputs environment variables
|
150
|
+
env_hook_file = File.join(main_repo_path, '.worktree.yml')
|
151
|
+
env_hook_config = <<~YAML
|
152
|
+
hooks:
|
153
|
+
pre_add:
|
154
|
+
commands:
|
155
|
+
- "echo \\"PATH=$WORKTREE_PATH BRANCH=$WORKTREE_BRANCH ROOT=$WORKTREE_MAIN\\""
|
156
|
+
YAML
|
157
|
+
File.write(env_hook_file, env_hook_config)
|
158
|
+
|
159
|
+
hook_manager = WorktreeManager::HookManager.new(main_repo_path)
|
160
|
+
|
161
|
+
# Verify environment variables during hook execution
|
162
|
+
expect do
|
163
|
+
hook_manager.execute_hook(:pre_add, path: worktree_path, branch: branch_name)
|
164
|
+
end.to output(%r{PATH=.*test-worktree.*BRANCH=.*test/branch.*ROOT=.*main-repo}).to_stdout
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe 'CLI integration tests' do
|
169
|
+
it 'complete workflow works correctly through CLI' do
|
170
|
+
cli = WorktreeManager::CLI.new
|
171
|
+
|
172
|
+
# Set up CLI environment
|
173
|
+
allow(cli).to receive(:main_repository?).and_return(true)
|
174
|
+
# Stub exit to prevent actual process termination
|
175
|
+
allow(cli).to receive(:exit)
|
176
|
+
|
177
|
+
# Execute add command
|
178
|
+
expect do
|
179
|
+
cli.invoke(:add, [worktree_path], { branch: branch_name })
|
180
|
+
end.to output(/Worktree created:.*test-worktree/).to_stdout
|
181
|
+
|
182
|
+
# Execute list command
|
183
|
+
expect do
|
184
|
+
cli.list
|
185
|
+
end.to output(/test-worktree/).to_stdout
|
186
|
+
|
187
|
+
# Execute remove command
|
188
|
+
# Git stores relative paths, so remove using relative path
|
189
|
+
relative_path = Pathname.new(worktree_path).relative_path_from(Pathname.new(main_repo_path)).to_s
|
190
|
+
expect do
|
191
|
+
cli.remove(relative_path)
|
192
|
+
end.to output(/Worktree removed:.*test-worktree/).to_stdout
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe 'worktrees_dir feature integration tests' do
|
197
|
+
let(:worktrees_dir) { '../worktrees' }
|
198
|
+
let(:worktree_name) { "feature-branch-#{test_id}" }
|
199
|
+
let(:expected_path) { File.expand_path(File.join(worktrees_dir, worktree_name), main_repo_path) }
|
200
|
+
|
201
|
+
before do
|
202
|
+
# Add worktrees_dir configuration
|
203
|
+
config_with_dir = <<~YAML
|
204
|
+
worktrees_dir: "#{worktrees_dir}"
|
205
|
+
hooks:
|
206
|
+
post_add:
|
207
|
+
commands:
|
208
|
+
- "echo 'Worktree created at $WORKTREE_ABSOLUTE_PATH'"
|
209
|
+
YAML
|
210
|
+
File.write(hook_file, config_with_dir)
|
211
|
+
end
|
212
|
+
|
213
|
+
it 'creates worktree according to worktrees_dir setting' do
|
214
|
+
cli = WorktreeManager::CLI.new
|
215
|
+
allow(cli).to receive(:main_repository?).and_return(true)
|
216
|
+
allow(cli).to receive(:exit)
|
217
|
+
|
218
|
+
# Add worktree with name only
|
219
|
+
expect do
|
220
|
+
cli.invoke(:add, [worktree_name], { branch: worktree_name })
|
221
|
+
end.to output(/Worktree created:.*#{worktree_name}/).to_stdout
|
222
|
+
|
223
|
+
# Verify created at correct location
|
224
|
+
expect(Dir.exist?(expected_path)).to be true
|
225
|
+
|
226
|
+
# Cleanup
|
227
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
228
|
+
manager.remove(expected_path)
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'removes worktree using worktrees_dir setting' do
|
232
|
+
# Create worktree first
|
233
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
234
|
+
FileUtils.mkdir_p(File.dirname(expected_path))
|
235
|
+
manager.add_with_new_branch(expected_path, worktree_name)
|
236
|
+
|
237
|
+
cli = WorktreeManager::CLI.new
|
238
|
+
allow(cli).to receive(:main_repository?).and_return(true)
|
239
|
+
allow(cli).to receive(:exit)
|
240
|
+
|
241
|
+
# Remove worktree with name only
|
242
|
+
expect do
|
243
|
+
cli.remove(worktree_name)
|
244
|
+
end.to output(/Worktree removed:.*#{worktree_name}/).to_stdout
|
245
|
+
|
246
|
+
expect(Dir.exist?(expected_path)).to be false
|
247
|
+
end
|
248
|
+
|
249
|
+
it 'processes relative path based on repository' do
|
250
|
+
cli = WorktreeManager::CLI.new
|
251
|
+
allow(cli).to receive(:main_repository?).and_return(true)
|
252
|
+
allow(cli).to receive(:exit)
|
253
|
+
|
254
|
+
relative_path = '../custom/worktree'
|
255
|
+
expected_custom_path = File.expand_path(relative_path, main_repo_path)
|
256
|
+
|
257
|
+
# Add worktree with relative path
|
258
|
+
expect do
|
259
|
+
cli.invoke(:add, [relative_path], { branch: 'custom-branch' })
|
260
|
+
end.to output(%r{Worktree created:.*custom/worktree}).to_stdout
|
261
|
+
|
262
|
+
expect(Dir.exist?(expected_custom_path)).to be true
|
263
|
+
|
264
|
+
# Cleanup
|
265
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
266
|
+
manager.remove(expected_custom_path)
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'uses absolute path as is when provided' do
|
270
|
+
cli = WorktreeManager::CLI.new
|
271
|
+
allow(cli).to receive(:main_repository?).and_return(true)
|
272
|
+
allow(cli).to receive(:exit)
|
273
|
+
|
274
|
+
absolute_path = File.join(test_dir, 'absolute-worktree')
|
275
|
+
|
276
|
+
# Add worktree with absolute path
|
277
|
+
expect do
|
278
|
+
cli.invoke(:add, [absolute_path], { branch: 'absolute-branch' })
|
279
|
+
end.to output(/Worktree created:.*absolute-worktree/).to_stdout
|
280
|
+
|
281
|
+
expect(Dir.exist?(absolute_path)).to be true
|
282
|
+
|
283
|
+
# Cleanup
|
284
|
+
manager = WorktreeManager::Manager.new(main_repo_path)
|
285
|
+
manager.remove(absolute_path)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'worktree_manager'
|
3
|
+
require 'worktree_manager/cli'
|
4
|
+
require 'worktree_manager/hook_manager'
|
5
|
+
require 'worktree_manager/manager'
|
6
|
+
require 'worktree_manager/worktree'
|
7
|
+
require 'worktree_manager/config_manager'
|
8
|
+
require 'tty-prompt'
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.expect_with :rspec do |c|
|
12
|
+
c.syntax = :expect
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# SystemExit handling in tests
|
17
|
+
#
|
18
|
+
# This project's tests often involve CLI commands that call exit.
|
19
|
+
# There are two approaches to handle this:
|
20
|
+
#
|
21
|
+
# 1. Integration tests (spec/integration/)
|
22
|
+
# - Stub the exit method on CLI instance: `allow(cli).to receive(:exit)`
|
23
|
+
# - This prevents exit from doing anything, allowing continued execution
|
24
|
+
# - Useful for testing complete workflows
|
25
|
+
#
|
26
|
+
# 2. Unit tests (spec/worktree_manager/)
|
27
|
+
# - Individually stub in tests that expect SystemExit
|
28
|
+
# - Example: stub_exit helper method in cli_spec.rb
|
29
|
+
# - `allow(cli).to receive(:exit) { |code| raise SystemExit.new(code) }`
|
30
|
+
# - This enables `expect { }.to raise_error(SystemExit)` tests
|
31
|
+
#
|
32
|
+
# Important notes:
|
33
|
+
# - Don't stub exit globally (tests may terminate early)
|
34
|
+
# - Choose the appropriate method based on test purpose
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe WorktreeManager::CLI do
|
4
|
+
describe 'help functionality' do
|
5
|
+
let(:help_output) { capture_stdout { WorktreeManager::CLI.start(args) } }
|
6
|
+
|
7
|
+
context "when using 'wm help add'" do
|
8
|
+
let(:args) { %w[help add] }
|
9
|
+
|
10
|
+
it 'displays help for the add command' do
|
11
|
+
expect(help_output).to include('Usage:')
|
12
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
13
|
+
expect(help_output).to include('Description:')
|
14
|
+
expect(help_output).to include('creates a new git worktree')
|
15
|
+
expect(help_output).to include('--branch')
|
16
|
+
expect(help_output).to include('--track')
|
17
|
+
expect(help_output).to include('--force')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "when using 'wm add --help'" do
|
22
|
+
let(:args) { ['add', '--help'] }
|
23
|
+
|
24
|
+
it 'displays help for the add command instead of treating --help as an argument' do
|
25
|
+
expect(help_output).to include('Usage:')
|
26
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
27
|
+
expect(help_output).to include('Description:')
|
28
|
+
expect(help_output).to include('creates a new git worktree')
|
29
|
+
expect(help_output).not_to include('Error')
|
30
|
+
expect(help_output).not_to include('invalid reference: --help')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "when using 'wm add -h'" do
|
35
|
+
let(:args) { ['add', '-h'] }
|
36
|
+
|
37
|
+
it 'displays help for the add command' do
|
38
|
+
expect(help_output).to include('Usage:')
|
39
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context "when using 'wm add -?'" do
|
44
|
+
let(:args) { ['add', '-?'] }
|
45
|
+
|
46
|
+
it 'displays help for the add command' do
|
47
|
+
expect(help_output).to include('Usage:')
|
48
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when using 'wm add --usage'" do
|
53
|
+
let(:args) { ['add', '--usage'] }
|
54
|
+
|
55
|
+
it 'displays help for the add command' do
|
56
|
+
expect(help_output).to include('Usage:')
|
57
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "when using 'wm remove --help'" do
|
62
|
+
let(:args) { ['remove', '--help'] }
|
63
|
+
|
64
|
+
it 'displays help for the remove command' do
|
65
|
+
expect(help_output).to include('Usage:')
|
66
|
+
expect(help_output).to include('remove [NAME_OR_PATH]')
|
67
|
+
expect(help_output).to include('Remove an existing worktree')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when using 'wm list --help'" do
|
72
|
+
let(:args) { ['list', '--help'] }
|
73
|
+
|
74
|
+
it 'displays help for the list command' do
|
75
|
+
expect(help_output).to include('Usage:')
|
76
|
+
expect(help_output).to include('list')
|
77
|
+
expect(help_output).to include('List all worktrees')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when using just 'wm help'" do
|
82
|
+
let(:args) { ['help'] }
|
83
|
+
|
84
|
+
it 'displays general help with all commands' do
|
85
|
+
expect(help_output).to include('Commands:')
|
86
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
87
|
+
expect(help_output).to include('remove [NAME_OR_PATH]')
|
88
|
+
expect(help_output).to include('list')
|
89
|
+
expect(help_output).to include('version')
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "when using just 'wm --help'" do
|
94
|
+
let(:args) { ['--help'] }
|
95
|
+
|
96
|
+
it 'displays general help with all commands' do
|
97
|
+
expect(help_output).to include('Commands:')
|
98
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
99
|
+
expect(help_output).to include('remove [NAME_OR_PATH]')
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Edge case tests as recommended by o3
|
104
|
+
context 'edge cases' do
|
105
|
+
context 'when help flag is at the end with other options' do
|
106
|
+
let(:args) { ['add', 'foo', '--no-hooks', '-h'] }
|
107
|
+
|
108
|
+
it 'displays help for the add command' do
|
109
|
+
expect(help_output).to include('Usage:')
|
110
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
111
|
+
expect(help_output).not_to include('Error')
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context 'when using help with unknown command' do
|
116
|
+
let(:args) { ['foo', '--help'] }
|
117
|
+
|
118
|
+
it 'shows error for unknown command' do
|
119
|
+
expect { help_output }.to output(/Could not find command "foo"/).to_stderr
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'when using help command with unknown command' do
|
124
|
+
let(:args) { %w[help foo] }
|
125
|
+
|
126
|
+
it 'shows error for unknown command' do
|
127
|
+
expect { help_output }.to output(/Could not find command "foo"/).to_stderr
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when multiple help flags are provided' do
|
132
|
+
let(:args) { ['add', '--help', '-h', '-?'] }
|
133
|
+
|
134
|
+
it 'displays help for the add command only once' do
|
135
|
+
expect(help_output).to include('Usage:')
|
136
|
+
expect(help_output).to include('add NAME_OR_PATH [BRANCH]')
|
137
|
+
# Ensure help is not duplicated
|
138
|
+
expect(help_output.scan('Usage:').count).to eq(1)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
def capture_stdout
|
146
|
+
original_stdout = $stdout
|
147
|
+
$stdout = StringIO.new
|
148
|
+
yield
|
149
|
+
$stdout.string
|
150
|
+
ensure
|
151
|
+
$stdout = original_stdout
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|