git_snip 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +25 -0
- data/lib/git_snip/branch.rb +17 -8
- data/lib/git_snip/cli.rb +42 -11
- data/lib/git_snip/config.rb +23 -0
- data/lib/git_snip/version.rb +1 -1
- data/spec/lib/git_snip/branch_spec.rb +41 -22
- data/spec/lib/git_snip/cli_help_spec.rb +11 -0
- data/spec/lib/git_snip/cli_spec.rb +68 -0
- data/spec/lib/git_snip/config_spec.rb +24 -0
- data/spec/support/cli_helper.rb +5 -2
- data/spec/support/config_shared_context.rb +18 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a20a58119f68056d5761981e0440ba321231416d
|
4
|
+
data.tar.gz: 8c095dc7ee582d109391f0ab07dab8fe0c48fc13
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb771326cd8b86b479b7ce7c9ae1a7f23758327445370b65cc682e7537bf10a07ca08df7b0bf7bb2cbcd0be2feaa9e88ec7c5e106b0220d50d79c80d7b7b5b38
|
7
|
+
data.tar.gz: ce9d2f055e48b91d10422ffefcc7cf3983774a72b0c979a014773fb933a831ea1177ae13c61c00ea2dec62e7afe9f84d7b75eaf4d3203faff0409acaf26ab874
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## 0.0.4
|
4
|
+
|
5
|
+
* Add config file.
|
6
|
+
|
7
|
+
* Add `--full` option to show most branch information without cropping.
|
8
|
+
|
9
|
+
Thanks to @bgreg for the idea and suggestions. Thanks to
|
10
|
+
[Ruby Workshop](http://www.meetup.com/Silicone-Valley-Ruby-Workshop/) too.
|
11
|
+
|
3
12
|
## 0.0.3
|
4
13
|
|
5
14
|
* Fix missing new line after each branch line.
|
data/README.md
CHANGED
@@ -38,6 +38,11 @@ Delete branches already merged to master:
|
|
38
38
|
|
39
39
|
$ git snip -f
|
40
40
|
|
41
|
+
Delete branches already merged to master and show more detailed listing of
|
42
|
+
deleted branches:
|
43
|
+
|
44
|
+
$ git snip -f --full
|
45
|
+
|
41
46
|
Delete branches already merged to master, except staging and production:
|
42
47
|
|
43
48
|
$ git snip -f --ignore=staging production
|
@@ -50,6 +55,26 @@ Delete branches already merged to `branch_a`:
|
|
50
55
|
|
51
56
|
$ git snip -f --target=branch_a
|
52
57
|
|
58
|
+
You can also list the available options from command line:
|
59
|
+
|
60
|
+
$ git snip help
|
61
|
+
|
62
|
+
## Config file
|
63
|
+
|
64
|
+
If you want some arguments to always be set, add a file to the root of the
|
65
|
+
repository with name `.git_snip.yml`. Specify the default arguments as YAML in
|
66
|
+
the file like this:
|
67
|
+
|
68
|
+
```yaml
|
69
|
+
full: true
|
70
|
+
ignore:
|
71
|
+
- staging
|
72
|
+
- production
|
73
|
+
```
|
74
|
+
|
75
|
+
The config arguments are overridden when the same arguments are set from command
|
76
|
+
line.
|
77
|
+
|
53
78
|
## Contributing
|
54
79
|
|
55
80
|
1. Fork it ( https://github.com/htanata/git_snip/fork )
|
data/lib/git_snip/branch.rb
CHANGED
@@ -1,15 +1,24 @@
|
|
1
1
|
module GitSnip
|
2
2
|
module Branch
|
3
|
-
|
3
|
+
Row = Struct.new(:sha, :name, :date, :author, :message)
|
4
|
+
|
5
|
+
def self.row(branch)
|
6
|
+
Row.new.tap do |row|
|
7
|
+
row.sha = column(branch.gcommit.sha, 7)
|
8
|
+
row.name = column(branch.name, 12)
|
9
|
+
row.date = column(branch.gcommit.date.strftime('%F'), 10)
|
10
|
+
row.author = column(branch.gcommit.author.email.sub(/@.*/, ''), 8)
|
11
|
+
row.message = column(first_line(branch.gcommit.message), 39)
|
12
|
+
end
|
4
13
|
end
|
5
14
|
|
6
|
-
def self.
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
15
|
+
def self.full_row(branch)
|
16
|
+
Row.new.tap do |row|
|
17
|
+
row.sha = branch.gcommit.sha
|
18
|
+
row.name = branch.name
|
19
|
+
row.date = branch.gcommit.date.iso8601
|
20
|
+
row.author = branch.gcommit.author.email
|
21
|
+
row.message = first_line(branch.gcommit.message)
|
13
22
|
end
|
14
23
|
end
|
15
24
|
|
data/lib/git_snip/cli.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
require 'thor'
|
2
2
|
require 'git_snip/cleaner'
|
3
3
|
require 'git_snip/branch'
|
4
|
+
require 'git_snip/config'
|
4
5
|
|
5
6
|
module GitSnip
|
6
7
|
class CLI < Thor
|
7
8
|
include Thor::Actions
|
8
9
|
|
10
|
+
class << self
|
11
|
+
def help(shell, subcommand = 'snip')
|
12
|
+
command_help(shell, subcommand)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
9
16
|
option :force, type: :boolean, aliases: '-f',
|
10
17
|
desc: 'Will refuse to run unless given -f or -n.'
|
11
18
|
|
@@ -21,13 +28,16 @@ module GitSnip
|
|
21
28
|
option :ignore, type: :array, default: [],
|
22
29
|
desc: 'List of branches to ignore.'
|
23
30
|
|
31
|
+
option :full, type: :boolean,
|
32
|
+
desc: 'Show most branch information without cropping.'
|
33
|
+
|
24
34
|
desc '', 'Delete branches which have been merged to target.'
|
25
35
|
def snip
|
26
|
-
if
|
36
|
+
if opts[:dry_run]
|
27
37
|
return dry_run
|
28
38
|
end
|
29
39
|
|
30
|
-
if !
|
40
|
+
if !opts[:force]
|
31
41
|
say '-f option is needed to delete branches.', :red
|
32
42
|
exit 64
|
33
43
|
end
|
@@ -69,18 +79,39 @@ module GitSnip
|
|
69
79
|
end
|
70
80
|
end
|
71
81
|
|
72
|
-
def say_branch_info(branch)
|
73
|
-
Branch.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
82
|
+
def say_branch_info(branch, full = false)
|
83
|
+
row = opts[:full] ? Branch.full_row(branch) : Branch.row(branch)
|
84
|
+
|
85
|
+
say row.sha + ' ', :yellow
|
86
|
+
say row.name + ' ', :magenta
|
87
|
+
say row.date + ' ', :green
|
88
|
+
say row.author + ' ', [:blue, :bold]
|
89
|
+
say row.message.strip + "\n"
|
80
90
|
end
|
81
91
|
|
82
92
|
def cleaner_args
|
83
|
-
|
93
|
+
opts.values_at(:repo, :target, :ignore)
|
94
|
+
end
|
95
|
+
|
96
|
+
def opts
|
97
|
+
@opts ||= begin
|
98
|
+
config = Config.new(options[:repo])
|
99
|
+
|
100
|
+
options_dup = options.dup
|
101
|
+
|
102
|
+
options_dup.each_pair do |k, v|
|
103
|
+
if v.is_a?(Array) && v.empty?
|
104
|
+
config_value = config.options[k]
|
105
|
+
|
106
|
+
if config_value.is_a?(Array) && config_value.any?
|
107
|
+
options_dup[k] = config_value
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
Thor::CoreExt::HashWithIndifferentAccess.new(
|
113
|
+
config.options.merge(options_dup)).freeze
|
114
|
+
end
|
84
115
|
end
|
85
116
|
end
|
86
117
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module GitSnip
|
4
|
+
class Config
|
5
|
+
def initialize(repo_path = '.')
|
6
|
+
@repo_path = repo_path
|
7
|
+
end
|
8
|
+
|
9
|
+
def options
|
10
|
+
@options ||= read_file || {}
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def read_file
|
16
|
+
YAML.load_file(config_path) if File.exist?(config_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def config_path
|
20
|
+
"#{@repo_path.chomp('/')}/.git_snip.yml"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/git_snip/version.rb
CHANGED
@@ -1,42 +1,61 @@
|
|
1
1
|
require 'git_snip/branch'
|
2
2
|
|
3
3
|
RSpec.describe GitSnip::Branch do
|
4
|
-
|
5
|
-
|
4
|
+
describe '.row' do
|
5
|
+
it 'should return values with number of characters within 80' do
|
6
|
+
result = described_class.row(build_branch(build_row))
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
expect([
|
9
|
+
result.sha, result.name, result.author, result.date, result.message
|
10
|
+
].join(' ').size).to eq(80)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should remove message starting from the first new line' do
|
14
|
+
row = build_row(message: "First line\n\nThird line")
|
15
|
+
result = described_class.row(build_branch(row))
|
16
|
+
|
17
|
+
expect(result.message).to eq('First line' + (' ' * 29))
|
18
|
+
end
|
10
19
|
end
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
21
|
+
describe '.full_row' do
|
22
|
+
it 'should return values while keeping most of the words' do
|
23
|
+
row = build_row(
|
24
|
+
message: ('A' * 100) + "\n\n" + ('B' * 85),
|
25
|
+
author: 'would_you_kindly@example.com'
|
26
|
+
)
|
27
|
+
|
28
|
+
result = described_class.full_row(build_branch(row))
|
15
29
|
|
16
|
-
|
30
|
+
expect(result.sha).to eq(row.sha)
|
31
|
+
expect(result.name).to eq(row.name)
|
32
|
+
expect(result.author).to eq(row.author)
|
33
|
+
expect(result.date).to eq(row.date.iso8601)
|
34
|
+
expect(result.message).to eq('A' * 100)
|
35
|
+
end
|
17
36
|
end
|
18
37
|
|
19
|
-
def build_branch(
|
20
|
-
author = instance_double('Git::Author', email:
|
38
|
+
def build_branch(row)
|
39
|
+
author = instance_double('Git::Author', email: row.author)
|
21
40
|
|
22
41
|
gcommit = instance_double(
|
23
42
|
'Git::Gcommit',
|
24
|
-
sha:
|
25
|
-
date:
|
26
|
-
message:
|
43
|
+
sha: row.sha,
|
44
|
+
date: row.date,
|
45
|
+
message: row.message,
|
27
46
|
author: author
|
28
47
|
)
|
29
48
|
|
30
|
-
instance_double('Git::Branch', name:
|
49
|
+
instance_double('Git::Branch', name: row.name, gcommit: gcommit)
|
31
50
|
end
|
32
51
|
|
33
|
-
def
|
34
|
-
described_class::
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
52
|
+
def build_row(attrs = {})
|
53
|
+
described_class::Row.new.tap do |row|
|
54
|
+
row.sha = attrs[:sha] || '28720606978a7257f735ce67df50bc55ccf1c138'
|
55
|
+
row.name = attrs[:name] || 'a_pretty_long_branch_name'
|
56
|
+
row.author = attrs[:author] || 'inigo_montoya@example.com'
|
57
|
+
row.date = attrs[:date] || Time.now
|
58
|
+
row.message = attrs[:message] || 'Hello, my name is Inigo Montoya.'
|
40
59
|
end
|
41
60
|
end
|
42
61
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'git_snip/cli'
|
2
|
+
|
3
|
+
RSpec.describe GitSnip::CLI, 'help' do
|
4
|
+
include CliHelper
|
5
|
+
|
6
|
+
it 'should display help for snip subcommand' do
|
7
|
+
stdout, _, _ = git_snip('help', :blank_slate)
|
8
|
+
expect(stdout).to match('--force')
|
9
|
+
expect(stdout).to match('--dry-run')
|
10
|
+
end
|
11
|
+
end
|
@@ -139,4 +139,72 @@ RSpec.describe GitSnip::CLI do
|
|
139
139
|
expect(stdout).to match(/merged.+Version 2\n/)
|
140
140
|
end
|
141
141
|
end
|
142
|
+
|
143
|
+
describe 'with --full' do
|
144
|
+
it 'should not wrap the branch listing' do
|
145
|
+
setup_basic_repo
|
146
|
+
|
147
|
+
repo.commit_on_branch(
|
148
|
+
'branch_with_really_long_name', "#{'A' * 100}\n\n#{'B' * 100}")
|
149
|
+
|
150
|
+
repo.merge_to_master('branch_with_really_long_name')
|
151
|
+
|
152
|
+
stdout, _, _ = git_snip('-n --full')
|
153
|
+
|
154
|
+
expect(stdout).to match("#{'A' * 100}\n")
|
155
|
+
expect(stdout).to match('branch_with_really_long_name')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context 'with config file' do
|
160
|
+
include_context 'config'
|
161
|
+
|
162
|
+
let(:config_dir) { repo.path }
|
163
|
+
|
164
|
+
before do
|
165
|
+
remove_config(config_dir)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should handle boolean config options' do
|
169
|
+
setup_basic_repo
|
170
|
+
|
171
|
+
create_config(config_dir, 'dry_run' => true)
|
172
|
+
|
173
|
+
stdout, _, exitstatus = git_snip
|
174
|
+
|
175
|
+
expect(exitstatus).to eq(0)
|
176
|
+
expect(stdout).to match("Would delete the following branches...\n\n")
|
177
|
+
expect(stdout).to match("merged")
|
178
|
+
expect(stdout).to match("\n\nDone.")
|
179
|
+
expect(exitstatus).to eq(0)
|
180
|
+
|
181
|
+
expect(repo.branch_exists?('merged')).to be_truthy
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should set handle list config options' do
|
185
|
+
setup_basic_repo
|
186
|
+
|
187
|
+
create_config(config_dir, 'ignore' => ['merged'])
|
188
|
+
|
189
|
+
stdout, _, exitstatus = git_snip('--dry-run')
|
190
|
+
|
191
|
+
expect(stdout).to eq(
|
192
|
+
"Would delete the following branches...\n\n" \
|
193
|
+
"No branches would be deleted.\n"
|
194
|
+
)
|
195
|
+
|
196
|
+
expect(exitstatus).to eq(0)
|
197
|
+
end
|
198
|
+
|
199
|
+
specify 'command line arguments should override config' do
|
200
|
+
setup_basic_repo
|
201
|
+
|
202
|
+
create_config(config_dir, 'dry_run' => true)
|
203
|
+
|
204
|
+
stdout, _, exitstatus = git_snip('--no-dry-run')
|
205
|
+
|
206
|
+
expect(stdout).to eq("-f option is needed to delete branches.\n")
|
207
|
+
expect(exitstatus).to eq(64)
|
208
|
+
end
|
209
|
+
end
|
142
210
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'git_snip/config'
|
2
|
+
|
3
|
+
RSpec.describe GitSnip::Config do
|
4
|
+
include_context 'config'
|
5
|
+
|
6
|
+
let(:config_dir) { 'tmp' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
remove_config(config_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#options' do
|
13
|
+
it "should be an empty hash when config file doesn't exist" do
|
14
|
+
expect(described_class.new(config_dir).options).to eq({})
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should read content of config file and convert keys to symbol' do
|
18
|
+
options = { 'foo' => 'bar' }
|
19
|
+
create_config(config_dir, options)
|
20
|
+
|
21
|
+
expect(described_class.new(config_dir).options).to eq('foo' => 'bar')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/spec/support/cli_helper.rb
CHANGED
@@ -14,11 +14,14 @@ module CliHelper
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
def git_snip(args = '')
|
17
|
+
def git_snip(args = '', blank_slate = false)
|
18
18
|
stdin, stdout, stderr = Array.new(3) { StringIO.new }
|
19
19
|
|
20
|
+
arguments = args.dup
|
21
|
+
arguments << " --repo=#{repo.path}" unless blank_slate
|
22
|
+
|
20
23
|
runner =
|
21
|
-
GitSnip::CLIRunner.new(
|
24
|
+
GitSnip::CLIRunner.new(arguments.split(' '),
|
22
25
|
stdin, stdout, stderr, FakeKernel.new)
|
23
26
|
|
24
27
|
exitstatus = runner.execute!
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
shared_context 'config' do
|
4
|
+
def create_config(config_dir, options)
|
5
|
+
FileUtils.mkdir_p(config_dir)
|
6
|
+
File.open(config_path(config_dir), 'w') { |f| f.write(options.to_yaml) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def remove_config(config_dir)
|
10
|
+
path = config_path(config_dir)
|
11
|
+
|
12
|
+
FileUtils.rm_f(path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def config_path(config_dir)
|
16
|
+
"#{config_dir}/.git_snip.yml"
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_snip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hendy Tanata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: git
|
@@ -102,13 +102,17 @@ files:
|
|
102
102
|
- lib/git_snip/branch.rb
|
103
103
|
- lib/git_snip/cleaner.rb
|
104
104
|
- lib/git_snip/cli.rb
|
105
|
+
- lib/git_snip/config.rb
|
105
106
|
- lib/git_snip/version.rb
|
106
107
|
- spec/lib/git_snip/branch_spec.rb
|
107
108
|
- spec/lib/git_snip/cleaner_spec.rb
|
109
|
+
- spec/lib/git_snip/cli_help_spec.rb
|
108
110
|
- spec/lib/git_snip/cli_spec.rb
|
111
|
+
- spec/lib/git_snip/config_spec.rb
|
109
112
|
- spec/spec_helper.rb
|
110
113
|
- spec/support/cli_helper.rb
|
111
114
|
- spec/support/cli_runner.rb
|
115
|
+
- spec/support/config_shared_context.rb
|
112
116
|
- spec/support/repo.rb
|
113
117
|
homepage: https://github.com/htanata/git_snip
|
114
118
|
licenses:
|
@@ -137,8 +141,11 @@ summary: Clean obsolete branches on your local git repository safely
|
|
137
141
|
test_files:
|
138
142
|
- spec/lib/git_snip/branch_spec.rb
|
139
143
|
- spec/lib/git_snip/cleaner_spec.rb
|
144
|
+
- spec/lib/git_snip/cli_help_spec.rb
|
140
145
|
- spec/lib/git_snip/cli_spec.rb
|
146
|
+
- spec/lib/git_snip/config_spec.rb
|
141
147
|
- spec/spec_helper.rb
|
142
148
|
- spec/support/cli_helper.rb
|
143
149
|
- spec/support/cli_runner.rb
|
150
|
+
- spec/support/config_shared_context.rb
|
144
151
|
- spec/support/repo.rb
|