thicket 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ed32e78f45dbc2f887839f2210edb89e4c0a7385c457c1b6f4a13a19ab1c384e
4
+ data.tar.gz: cede744f06f5ec921aad06af06f3ea7efd55e68a749b0290da4ac16752431bcd
5
+ SHA512:
6
+ metadata.gz: 1275727f176525c11c885ee985d1df0009a9005708f509d0107a5b3f6c4a8378a854dc0e748961dc8ef7cf3544cb9c002dac04646e8bdc7ac9e8b78fbb3804ac
7
+ data.tar.gz: 5191534f823312b3a1f30d9392aaf92980e964cd0787a5aded3940b7073fc72c1fed5b47b239b3b17918eaf4b8feef2c4f48f358f1e333f7139bcf8b7002c5ca
data/bin/thicket ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "thicket"
6
+
7
+ parser = Thicket::OptionParser.new
8
+ Thicket::Log.new(parser.options).print
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "time"
5
+
6
+ module Thicket
7
+ class Log
8
+ LOG_PARSE_REGEX = /[a-f0-9]{7}.+?m(.+?) .+?m\{([\w ]+)\}.+?m (?:\((.+?)\))?.+?m(.+$)/.freeze
9
+
10
+ def initialize(options)
11
+ @options = options
12
+ end
13
+
14
+ # Gets a printable version of the log for purposes of printing to a
15
+ # terminal. This effectively builds the final printable log to display to
16
+ # the user.
17
+ def print
18
+ FileUtils.cd(git_working_directory)
19
+ `#{git_log_command}`.split("\n").each { |l| puts process_git_log_line(l) }
20
+ rescue Errno::EPIPE, SystemExit, Interrupt
21
+ exit
22
+ end
23
+
24
+ private
25
+
26
+ # Takes a single line of raw, colored git log output and manipulates it
27
+ # into the desired format.
28
+ def process_git_log_line(line)
29
+ @padding_char = @padding_char == " " ? "-" : " "
30
+
31
+ line.match(LOG_PARSE_REGEX) do |matcher|
32
+ process_date_time(matcher[1], line, matcher[3])
33
+ process_message_prefix(matcher[4], line) if @options[:color_prefixes]
34
+ process_author_name(matcher[2], line)
35
+ end
36
+
37
+ line
38
+ end
39
+
40
+ # Takes an input log string and a commit date/time and replaces it in the
41
+ # log string with a formatted version.
42
+ def process_date_time(time_string, line, have_refs)
43
+ seconds_ago = Time.now - Time.iso8601(time_string)
44
+ measure = TimeMeasure.measures.find { |m| m.threshold < seconds_ago }
45
+ quantity = (seconds_ago / measure.length).floor
46
+ to_sub = +"#{quantity}#{measure.abbreviation}".rjust(3)
47
+ to_sub << "\e[31m" if have_refs # add color if we have refs in this line
48
+ line.sub!(time_string, to_sub)
49
+ end
50
+
51
+ # Takes an input log string and commit message, finds commit messages
52
+ # prefixes, and darkens them.
53
+ def process_message_prefix(message, line)
54
+ prefix_regex = %r{^(?=.*[0-9])([A-Z\d-]+?[: \/])}
55
+ message.match(prefix_regex) do |matcher|
56
+ prefix = matcher[1]
57
+ line.sub!(/([^\/])#{prefix}/, "\\1\e[1;30m#{prefix}\e[m")
58
+ end
59
+ end
60
+
61
+ # Takes an input log string and commit author, and moves it from the normal
62
+ # position in the string to a right-justified location.
63
+ def process_author_name(author, line)
64
+ line.sub!("\e[34m{#{author}}\e[31m ", "")
65
+ total_length = strip_color(line).length
66
+ over = (total_length + author.length + 1) - terminal_width
67
+ line = line[0...-over] if over > 0
68
+
69
+ total_length = strip_color(line).length
70
+ spaces_needed = terminal_width - total_length - author.length - 2
71
+ if spaces_needed < 0
72
+ line = +"#{line[0...-spaces_needed - 5]}... "
73
+ else
74
+ line << " \e[30m"
75
+ line << @padding_char * spaces_needed
76
+ line << " \e[m"
77
+ end
78
+
79
+ line << "\e[34m#{author}\e[m"
80
+ end
81
+
82
+ # Strips ANSI color escape codes from a string. Colorize's
83
+ # String#uncolorize would be used, but it seems to only remove escape codes
84
+ # which match a strict pattern, which git log's colored output doesn't
85
+ # follow.
86
+ def strip_color(string)
87
+ color_escape_regex = /\e\[([;\d]+)?m/
88
+ string.gsub(color_escape_regex, "").chomp
89
+ end
90
+
91
+ # Gets the width of the terminal window in columns. Memoizes the result to
92
+ # avoid more shell calls, and because the terminal size won't be changing
93
+ # during the execution of this script.
94
+ def terminal_width
95
+ @terminal_width ||= `tput cols`.to_i
96
+ rescue Errno::ENOENT
97
+ puts "Failed to determine terminal column width."
98
+ exit
99
+ end
100
+
101
+ # The command string which gets the raw git log input straight from git.
102
+ # Includes all formatting and color escape codes.
103
+ def git_log_command
104
+ format = "%C(yellow)%h %Cgreen%aI %Cblue{%an}%Cred%d %Creset%s"
105
+ cmd = "#{git_executable} log --oneline --decorate --color " \
106
+ "--graph --pretty=format:'#{format}'"
107
+ cmd << " --all" if @options[:all]
108
+
109
+ cmd
110
+ end
111
+
112
+ # The path to the git executable. Honors the passed in command line option,
113
+ # and falls back to just "git".
114
+ def git_executable
115
+ @options[:git_binary] || "git"
116
+ end
117
+
118
+ # The directory which represents the git project that we want to retreive
119
+ # logs for. Uses the command line option if it was provided, and falls back
120
+ # to the current working directory otherwise.
121
+ def git_working_directory
122
+ @options[:project_directory] || Dir.pwd
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+
5
+ module Thicket
6
+ Options = Struct.new(:name)
7
+
8
+ class OptionParser
9
+ attr_reader :options
10
+
11
+ def initialize
12
+ @options = {}
13
+ parse ARGV
14
+ end
15
+
16
+ private
17
+
18
+ def parse(options)
19
+ args = Options.new("world")
20
+
21
+ opt_parser = ::OptionParser.new do |opts|
22
+ opts.banner = "Usage: thicket [options] <command>"
23
+
24
+ opts.on("-v", "--version", "Print the version number") do |v|
25
+ args.name = v
26
+ puts Thicket::VERSION
27
+ exit
28
+ end
29
+
30
+ opts.on("-h", "--help", "Prints this help") do
31
+ puts opts
32
+ exit
33
+ end
34
+
35
+ opts.on("-d", "--directory DIRECTORY", String, "Path to the project directory") do |project_directory|
36
+ args.name = project_directory
37
+ @options[:project_directory] = File.expand_path(project_directory)
38
+ end
39
+
40
+ opts.on("-a", "--all", TrueClass, "Displays all branches on all remotes.") do |all|
41
+ args.name = all
42
+ @options[:all] = all
43
+ end
44
+
45
+ opts.on("-p", "--color-prefixes", TrueClass, "Adds coloring to commit message prefixes.") do |prefixes|
46
+ args.name = prefixes
47
+ @options[:color_prefixes] = prefixes
48
+ end
49
+
50
+ opts.on("--git-binary BINARY", String, "Path to a git executable") do |git_binary|
51
+ args.name = git_binary
52
+ @options[:git_binary] = File.expand_path(git_binary)
53
+ end
54
+ end
55
+
56
+ opt_parser.parse!(options)
57
+ rescue ::OptionParser::ParseError => e
58
+ puts e.message
59
+ exit
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thicket
4
+ module TimeMeasure
5
+ class Measure
6
+ attr_reader :length, :abbreviation, :threshold
7
+
8
+ def initialize(length_in_seconds, abbreviation, threshold_in_seconds)
9
+ @length = length_in_seconds
10
+ @abbreviation = abbreviation
11
+ @threshold = threshold_in_seconds
12
+ end
13
+ end
14
+
15
+ YEAR = Measure.new(31_104_000, "Y", 155_520_000)
16
+ MONTH = Measure.new(2_592_000, "M", 15_552_000)
17
+ WEEK = Measure.new(604_800, "w", 2_419_200)
18
+ DAY = Measure.new(86_400, "d", 172_800)
19
+ HOUR = Measure.new(3_600, "h", 3_600)
20
+ MINUTE = Measure.new(60, "m", 60)
21
+ SECOND = Measure.new(1, "s", 0)
22
+
23
+ def self.measures
24
+ [YEAR, MONTH, WEEK, DAY, HOUR, MINUTE, SECOND]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Thicket
4
+ #:nodoc:
5
+ VERSION ||= "0.1.0"
6
+ end
data/lib/thicket.rb ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thicket/version"
4
+ require "thicket/log"
5
+ require "thicket/option_parser"
6
+ require "thicket/time_measure"
7
+
8
+ module Thicket
9
+ def self.root
10
+ File.expand_path("..", __dir__)
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thicket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Taylor Thurlow
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: guard
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: guard-rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rufo
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.5.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.5.1
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: solargraph
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: |
112
+ Git's default log command gets the job done, but its formatting capabilities
113
+ sometimes leave something to be desired. Thicket is an opinionated replacement for
114
+ "git log".
115
+ email: taylorthurlow8@gmail.com
116
+ executables:
117
+ - thicket
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - bin/thicket
122
+ - lib/thicket.rb
123
+ - lib/thicket/log.rb
124
+ - lib/thicket/option_parser.rb
125
+ - lib/thicket/time_measure.rb
126
+ - lib/thicket/version.rb
127
+ homepage: https://github.com/taylorthurlow/thicket
128
+ licenses:
129
+ - MIT
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '2.3'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubygems_version: 3.0.3
147
+ signing_key:
148
+ specification_version: 4
149
+ summary: An opinionated replacement for git's log command.
150
+ test_files: []