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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a6996bc4363bed00c0ed66d7ac49427a681f633e199e3b8f7b0e504bed64af52
4
- data.tar.gz: 5a1766c37e6460a3b6081ba876443359b8fe01c44a898e234a4a96527ac13306
3
+ metadata.gz: 81714cf7534f51e20f211a41f3b93173c51fc066c8605e8fdd3c4b2887208309
4
+ data.tar.gz: 2325066c76763a77dcc7734120da18d0cc71083b5f8586d4d10d8ae7cf647abd
5
5
  SHA512:
6
- metadata.gz: ed0bee6ea221fb9d3a50b8432151409604d0fee5ee63c7c423daf19fade386bc43a1c5d2645a178bdf9bc873d4318ec82d9f2468506f9b0643a59b9f48308f59
7
- data.tar.gz: 909f9f0e1bc401c5e110a830b910192fd7fc959fc457ab8b1d19e5d7db0b1b0e6b1aa7a2d558f4d253b70a0ceb6dd8633d2152f7eaaf670c10355bee3255f321
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
1
+ 0.7.2
data/lib/git_curate.rb CHANGED
@@ -2,6 +2,7 @@ require "git_curate/app"
2
2
  require "git_curate/branch"
3
3
  require "git_curate/cli_parser"
4
4
  require "git_curate/copyright"
5
+ require "git_curate/exceptions"
5
6
  require "git_curate/runner"
6
7
  require "git_curate/util"
7
8
  require "git_curate/version"
@@ -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 0 unless continue
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
- 1
23
+ EXIT_FAILURE
18
24
  end
19
25
 
20
26
  end
@@ -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
- # raw_name should start in "* " if the current branch, but should otherwise have not whitespace.
16
- def initialize(raw_name)
17
- @raw_name = raw_name
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 last_author
37
- Util.command_output("git log -n1 --format=format:%an #{proper_name} --")
45
+ def last_commit_date
46
+ initialize_last_commit_data
47
+ @last_commit_date
38
48
  end
39
49
 
40
- def last_commit_date
41
- Util.command_output("git log -n1 --date=short --format=format:%cd #{proper_name} --")
50
+ def last_author
51
+ initialize_last_commit_data
52
+ @last_author
42
53
  end
43
54
 
44
55
  def last_subject
45
- Util.command_output("git log -n1 --format=format:%s #{proper_name} --")
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
- command_to_branches("git branch")
51
- end
62
+ merged_branch_raw_names = Util.command_to_a("git branch --merged").to_set
52
63
 
53
- # Returns local branches that are merged into current HEAD
54
- def self.local_merged
55
- command_to_branches("git branch --merged")
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
- # Returns a Hash containing, as keys, the proper names of all local branches that have upstream branches,
59
- # and, as values, a brief description of each branch's status relative to its upstream
60
- # branch (up to date, or ahead/behind)
61
- def self.upstream_info
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.gsub!(LEADING_STAR_REGEX, "")
64
- branch_name = line.split(BRANCH_NAME_REGEX)[0]
65
- remote_info = line[REMOTE_INFO_REGEX, 1]
66
- if remote_info.nil?
67
- nil
68
- else
69
- comparison_raw = remote_info.split(":")
70
- comparison = if comparison_raw.length < 2
71
- "Up to date"
72
- else
73
- comparison_raw[1].strip.capitalize
74
- end
75
- [branch_name, comparison]
76
- end
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
- private
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
- def self.command_to_branches(command)
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
- # if and only if the program should continue after the options are passed.
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
@@ -5,15 +5,21 @@ require "tty-screen"
5
5
 
6
6
  module GitCurate
7
7
 
8
- EXIT_SUCCESS = 0
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| merged_branch_names.include?(b.proper_name) ? "Merged" : "Not merged" }
36
- t.add_column("Status vs#{$/}upstream") { |b| upstream_branches.fetch(b.proper_name, "No 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] "
@@ -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 status.exitstatus != 0
20
- raise RuntimeError.new(stderr_str)
20
+ if exit_status != EXIT_SUCCESS
21
+ raise SystemCommandError.new(stderr_str, exit_status)
21
22
  end
22
23
 
23
24
  stdout_str
@@ -1,3 +1,3 @@
1
1
  module GitCurate
2
- VERSION = "0.7.1"
2
+ VERSION = "0.7.2"
3
3
  end
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.1
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-03 00:00:00.000000000 Z
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