git_curate 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/VERSION +1 -1
- data/lib/git_curate.rb +1 -0
- data/lib/git_curate/app.rb +8 -2
- data/lib/git_curate/branch.rb +61 -36
- data/lib/git_curate/cli_parser.rb +4 -2
- data/lib/git_curate/exceptions.rb +16 -0
- data/lib/git_curate/runner.rb +11 -7
- data/lib/git_curate/util.rb +3 -2
- data/lib/git_curate/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 81714cf7534f51e20f211a41f3b93173c51fc066c8605e8fdd3c4b2887208309
|
4
|
+
data.tar.gz: 2325066c76763a77dcc7734120da18d0cc71083b5f8586d4d10d8ae7cf647abd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2a73379b744efb995a7aef5727f9be1b77617b26395f649eea0d16cfda72da49d675bfd8f270873e3d478918180e93d45e10fa43078f08da0d0eab3b43ad38c
|
7
|
+
data.tar.gz: 822e799c402d44bd7bb7d7e32d12c88bf411385e628d3d2bfdfba9479b95c7d3b5e4457903f13df2fecabe9508cd439cfa5c753f15bbff43c6a0b94fd0d6d6bf
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### v0.7.2
|
4
|
+
|
5
|
+
* Nicer error messages; for example, if `git curate` is run outside of a git repo, the user will now see
|
6
|
+
the same error message as they would see from running `git branch` (as opposed to seeing an unsightly stack trace)
|
7
|
+
* Performance improvements: it now runs faster, as the number of system calls made by the
|
8
|
+
application is reduced
|
9
|
+
* Improvements to code structure and internal documentation
|
10
|
+
|
3
11
|
### v0.7.1
|
4
12
|
|
5
13
|
* Fix errors on -h, -v and --version options due to incorrect exit code handling
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.2
|
data/lib/git_curate.rb
CHANGED
data/lib/git_curate/app.rb
CHANGED
@@ -4,17 +4,23 @@ module GitCurate
|
|
4
4
|
|
5
5
|
module App
|
6
6
|
|
7
|
+
# Runs the application and returns an exit status which should be passed to exit() by
|
8
|
+
# the caller of this function.
|
7
9
|
def self.main
|
8
10
|
parser = GitCurate::CLIParser.new
|
9
11
|
continue = parser.parse(ARGV) # will throw on error
|
10
|
-
return
|
12
|
+
return EXIT_SUCCESS unless continue
|
11
13
|
|
12
14
|
runner = GitCurate::Runner.new(parser.parsed_options)
|
15
|
+
# Args not parsed by the CLIParser are passed to Runner.
|
13
16
|
runner.run(ARGV)
|
17
|
+
rescue SystemCommandError => error
|
18
|
+
$stderr.puts(error.message)
|
19
|
+
error.exit_status
|
14
20
|
rescue OptionParser::InvalidOption
|
15
21
|
puts "Unrecognized option"
|
16
22
|
puts "For help, enter `git curate -h`"
|
17
|
-
|
23
|
+
EXIT_FAILURE
|
18
24
|
end
|
19
25
|
|
20
26
|
end
|
data/lib/git_curate/branch.rb
CHANGED
@@ -10,21 +10,30 @@ module GitCurate
|
|
10
10
|
LEADING_STAR_REGEX = /^\* /
|
11
11
|
REMOTE_INFO_REGEX = /^[^\s]+\s+[^\s]+\s+\[(.+?)\]/
|
12
12
|
|
13
|
+
# Returns the branch name, with "* " prefixed if it's the current branch.
|
13
14
|
attr_reader :raw_name
|
14
15
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
end
|
16
|
+
# Returns a human-friendly string describing the status of the branch relative to the upstream branch
|
17
|
+
# it's tracking, if any.
|
18
|
+
attr_reader :upstream_info
|
19
19
|
|
20
|
+
# Returns simply the name of the branch, without any other "decoration".
|
20
21
|
def proper_name
|
21
22
|
@proper_name ||= @raw_name.lstrip.sub(CURRENT_BRANCH_REGEX, '')
|
22
23
|
end
|
23
24
|
|
25
|
+
# Returns truthy if and only if this is the currently checked out branch.
|
24
26
|
def current?
|
25
27
|
@current ||= (@raw_name =~ CURRENT_BRANCH_REGEX)
|
26
28
|
end
|
27
29
|
|
30
|
+
# Return truthy if and only if this branch has been merged into the current HEAD.
|
31
|
+
def merged?
|
32
|
+
@merged
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the branch's name, with a prefix of "* " if it's the currently checked out branch, or else a prefix
|
36
|
+
# of " ". Branch displayable names are designed to be aligned with each other for display in a vertical column.
|
28
37
|
def displayable_name(pad:)
|
29
38
|
if pad && !current?
|
30
39
|
" #{@raw_name}"
|
@@ -33,58 +42,74 @@ module GitCurate
|
|
33
42
|
end
|
34
43
|
end
|
35
44
|
|
36
|
-
def
|
37
|
-
|
45
|
+
def last_commit_date
|
46
|
+
initialize_last_commit_data
|
47
|
+
@last_commit_date
|
38
48
|
end
|
39
49
|
|
40
|
-
def
|
41
|
-
|
50
|
+
def last_author
|
51
|
+
initialize_last_commit_data
|
52
|
+
@last_author
|
42
53
|
end
|
43
54
|
|
44
55
|
def last_subject
|
45
|
-
|
56
|
+
initialize_last_commit_data
|
57
|
+
@last_subject
|
46
58
|
end
|
47
59
|
|
48
60
|
# Returns the local branches
|
49
61
|
def self.local
|
50
|
-
|
51
|
-
end
|
62
|
+
merged_branch_raw_names = Util.command_to_a("git branch --merged").to_set
|
52
63
|
|
53
|
-
|
54
|
-
|
55
|
-
|
64
|
+
branch_info.map do |raw_name, info|
|
65
|
+
new(raw_name, merged: merged_branch_raw_names.include?(raw_name), upstream_info: info)
|
66
|
+
end
|
56
67
|
end
|
57
68
|
|
58
|
-
|
59
|
-
|
60
|
-
#
|
61
|
-
|
69
|
+
private
|
70
|
+
|
71
|
+
# Returns a Hash containing, as keys, the raw names of all local branches and, as values,
|
72
|
+
# a brief description of each branch's status relative to its upstream branch (up to
|
73
|
+
# date, or ahead/behind).
|
74
|
+
def self.branch_info
|
62
75
|
Util.command_to_a("git branch -vv").map do |line|
|
63
|
-
line
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end.compact.to_h
|
76
|
+
line_is_current_branch = (line =~ LEADING_STAR_REGEX)
|
77
|
+
tidied_line = (line_is_current_branch ? line.gsub(LEADING_STAR_REGEX, "") : line)
|
78
|
+
proper_branch_name = tidied_line.split(BRANCH_NAME_REGEX)[0]
|
79
|
+
raw_branch_name = (line_is_current_branch ? "* #{proper_branch_name}" : proper_branch_name)
|
80
|
+
remote_info = tidied_line[REMOTE_INFO_REGEX, 1]
|
81
|
+
upstream_info =
|
82
|
+
if remote_info.nil?
|
83
|
+
"No upstream"
|
84
|
+
else
|
85
|
+
comparison_raw = remote_info.split(":")
|
86
|
+
comparison_raw.length < 2 ? "Up to date" : comparison_raw[1].strip.capitalize
|
87
|
+
end
|
88
|
+
[raw_branch_name, upstream_info]
|
89
|
+
end.to_h
|
78
90
|
end
|
79
91
|
|
80
92
|
def self.delete_multi(*branches)
|
81
93
|
Util.command_output("git branch -D #{branches.map(&:proper_name).join(" ")} --")
|
82
94
|
end
|
83
95
|
|
84
|
-
|
96
|
+
# raw_name should start in "* " if the current branch, but should otherwise have not whitespace.
|
97
|
+
def initialize(raw_name, merged:, upstream_info:)
|
98
|
+
@raw_name = raw_name
|
99
|
+
@merged = merged
|
100
|
+
@upstream_info = upstream_info
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns an array with [date, author, subject], each as a string.
|
104
|
+
def initialize_last_commit_data
|
105
|
+
return if @last_commit_data
|
106
|
+
|
107
|
+
# For Windows compatibility we need double quotes around the format string, as well as spaces
|
108
|
+
# between the placeholders.
|
109
|
+
command = %Q(git log -n1 --date=short --format=format:"%cd %n %an %n %s" #{proper_name} --)
|
110
|
+
@last_commit_data = Util.command_to_a(command)
|
85
111
|
|
86
|
-
|
87
|
-
Util.command_to_a(command).map { |raw_branch_name| self.new(raw_branch_name) }
|
112
|
+
@last_commit_date, @last_author, @last_subject = @last_commit_data
|
88
113
|
end
|
89
114
|
|
90
115
|
end
|
@@ -10,8 +10,10 @@ module GitCurate
|
|
10
10
|
@parsed_options = {}
|
11
11
|
end
|
12
12
|
|
13
|
-
# Sets @parsed_options according to the options received, and return truthy
|
14
|
-
#
|
13
|
+
# Sets @parsed_options according to the options received, and return truthy if and only if the program should
|
14
|
+
# continue after the options are passed. Usually this method should be passed ARGV. Any elements of `options` that
|
15
|
+
# are not processed by this method, will remain in `options`; elements that are processed, will be destructively
|
16
|
+
# removed from `options`.
|
15
17
|
def parse(options)
|
16
18
|
opt_parser = OptionParser.new do |opts|
|
17
19
|
opts.banner = <<-EOF
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module GitCurate
|
2
|
+
|
3
|
+
EXIT_SUCCESS = 0
|
4
|
+
EXIT_FAILURE = 1
|
5
|
+
|
6
|
+
# Error indicating that a system command has exited with a non-success exit status.
|
7
|
+
class SystemCommandError < StandardError
|
8
|
+
|
9
|
+
attr_reader :exit_status
|
10
|
+
|
11
|
+
def initialize(message, exit_status)
|
12
|
+
super(message)
|
13
|
+
@exit_status = exit_status
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/git_curate/runner.rb
CHANGED
@@ -5,15 +5,21 @@ require "tty-screen"
|
|
5
5
|
|
6
6
|
module GitCurate
|
7
7
|
|
8
|
-
|
9
|
-
EXIT_FAILURE = 1
|
10
|
-
|
8
|
+
# Contains the main logic of the application.
|
11
9
|
class Runner
|
12
10
|
|
11
|
+
# Accepts a Hash of options passed from the parsed command line. Currently there is only one option, :list,
|
12
|
+
# which is treated as a boolean, and determines whether the branches will simply be listed non-interactively
|
13
|
+
# (list: true), or interactively with opportunities for the user to select branches for deletion (list: false).
|
13
14
|
def initialize(opts)
|
14
15
|
@opts = opts
|
15
16
|
end
|
16
17
|
|
18
|
+
# Runs the application, listing branches either interactively or non-interactively. Returns an exit status,
|
19
|
+
# suitable for passing to `exit()`.
|
20
|
+
# `args` should be passed an array of non-option/non-flag arguments received from the command
|
21
|
+
# line. If this array is of inappropriate length, EXIT_FAILURE will be returned. (The
|
22
|
+
# appropriate length may be 0.)
|
17
23
|
def run(args)
|
18
24
|
if args.length != 0
|
19
25
|
$stderr.puts "This script does not accept any arguments."
|
@@ -22,8 +28,6 @@ module GitCurate
|
|
22
28
|
|
23
29
|
branches = Branch.local
|
24
30
|
branches.reject!(&:current?) if interactive?
|
25
|
-
merged_branch_names = Branch.local_merged.map(&:proper_name).to_set
|
26
|
-
upstream_branches = Branch.upstream_info
|
27
31
|
|
28
32
|
table = Tabulo::Table.new(branches, vertical_rule_character: " ", intersection_character: " ",
|
29
33
|
horizontal_rule_character: "-", column_padding: 0, align_header: :left) do |t|
|
@@ -32,8 +36,8 @@ module GitCurate
|
|
32
36
|
t.add_column("Last commit", &:last_commit_date)
|
33
37
|
t.add_column("Last author", &:last_author)
|
34
38
|
t.add_column("Last subject", &:last_subject)
|
35
|
-
t.add_column("Merged#{$/}into HEAD?") { |b|
|
36
|
-
t.add_column("Status vs#{$/}upstream"
|
39
|
+
t.add_column("Merged#{$/}into HEAD?") { |b| b.merged? ? "Merged" : "Not merged" }
|
40
|
+
t.add_column("Status vs#{$/}upstream", &:upstream_info)
|
37
41
|
end
|
38
42
|
|
39
43
|
prompt = " Delete? [y/N/done/abort/help] "
|
data/lib/git_curate/util.rb
CHANGED
@@ -15,9 +15,10 @@ module GitCurate
|
|
15
15
|
# output as its message.
|
16
16
|
def self.command_output(command)
|
17
17
|
stdout_str, stderr_str, status = Open3.capture3(command)
|
18
|
+
exit_status = status.exitstatus
|
18
19
|
|
19
|
-
if
|
20
|
-
raise
|
20
|
+
if exit_status != EXIT_SUCCESS
|
21
|
+
raise SystemCommandError.new(stderr_str, exit_status)
|
21
22
|
end
|
22
23
|
|
23
24
|
stdout_str
|
data/lib/git_curate/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: git_curate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Harvey
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-08-
|
11
|
+
date: 2019-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- lib/git_curate/branch.rb
|
164
164
|
- lib/git_curate/cli_parser.rb
|
165
165
|
- lib/git_curate/copyright.rb
|
166
|
+
- lib/git_curate/exceptions.rb
|
166
167
|
- lib/git_curate/runner.rb
|
167
168
|
- lib/git_curate/util.rb
|
168
169
|
- lib/git_curate/version.rb
|