git_snip 0.0.3 → 0.0.4
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 +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
|