thicket 0.1.0
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 +7 -0
- data/bin/thicket +8 -0
- data/lib/thicket/log.rb +125 -0
- data/lib/thicket/option_parser.rb +62 -0
- data/lib/thicket/time_measure.rb +27 -0
- data/lib/thicket/version.rb +6 -0
- data/lib/thicket.rb +12 -0
- metadata +150 -0
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
data/lib/thicket/log.rb
ADDED
@@ -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
|
data/lib/thicket.rb
ADDED
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: []
|