Skip to content

Make the change easy #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 12 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ With the hook initialized in a repository, create branches being sure to include
the Pivotal Tracker story number in the branch name.

```bash
$ git checkout -b a_useful_and_helpful_name_8675309
$ git switch -c best_feature_ever_8675309
```

When you commit, Git will fire the hook which will find the story number in the
branch name and prepare your commit message so that it includes the story number
in the [special Pivotal Tracker syntax][pt-format].

```bash
# on branch named `best_feature_ever-8675309`
# on branch named `best_feature_ever_8675309`
$ git commit
```

Expand All @@ -72,7 +72,7 @@ the top)*
[#8675309]
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch best_feature_ever-8675309
# On branch best_feature_ever_8675309
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
Expand All @@ -89,7 +89,7 @@ If you pass a commit message on the command line the hook will still add the
story number, preceded by an empty line, to the end of your message.

```bash
# on branch named `best_feature_ever-8675309`
# on branch named `best_feature_ever_8675309`
$ git commit -m'Look at this rad code, yo!'
```

Expand All @@ -105,7 +105,7 @@ However, if you include the story number in the Pivotal Tracker format within
your commit message, the hook will do nothing.

```bash
# on branch named `best_feature_ever-8675309`
# on branch named `best_feature_ever_8675309`
$ git commit -m'[#8675309] Look at this rad code, yo!'
```

Expand Down Expand Up @@ -156,12 +156,11 @@ name, optionally prefixing it with a hash (`#`). Examples:
## Contributing :octocat:

1. Fork it
2. Create your feature branch (`git checkout -b my_new_feature`)
2. Create your feature branch (`git switch -c my_new_feature`)
3. Commit your changes (`git commit -am 'Added some feature'`)
4. Push to the branch (`git push origin my_new_feature`)
5. Create new Pull Request


[pt]: https://www.pivotaltracker.com/
[pt-format]: https://www.pivotaltracker.com/help/api?version=v3#scm_post_commit_message_syntax
[tpope]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
Expand Down
19 changes: 12 additions & 7 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env rake
require File.expand_path("../lib/git_tracker/version", __FILE__)

require Pathname(".").join("lib/git_tracker/version").expand_path

# Skip these tasks when being installed by Homebrew
unless ENV["HOMEBREW_BREW_FILE"]
Expand All @@ -19,22 +20,26 @@ unless ENV["HOMEBREW_BREW_FILE"]
end

# standalone and Homebrew
file "git-tracker" => FileList.new("lib/git_tracker.rb", "lib/git_tracker/*.rb") do |task|
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
directory "pkg"

file "pkg/git-tracker" => Rake::FileList.new("pkg", "lib/git_tracker.rb", "lib/git_tracker/*.rb") do |task|
$LOAD_PATH.unshift(Pathname(__dir__).join("lib").expand_path)
require "git_tracker/standalone"
GitTracker::Standalone.save(task.name)

path, filename = task.name.split("/")
GitTracker::Standalone.save(filename, path: path)
end

namespace :standalone do
desc "Build standalone script"
task build: "git-tracker"
task build: "pkg/git-tracker"

desc "Build and install standalone script"
task install: "standalone:build" do
prefix = ENV["PREFIX"] || ENV["prefix"] || "/usr/local"

FileUtils.mkdir_p "#{prefix}/bin"
FileUtils.cp "git-tracker", "#{prefix}/bin", preserve: true
FileUtils.mkdir_p("#{prefix}/bin")
FileUtils.cp("pkg/git-tracker", "#{prefix}/bin", preserve: true)
end

task :homebrew do
Expand Down
2 changes: 1 addition & 1 deletion exe/git-tracker
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env ruby

require "git_tracker"
GitTracker::Runner.execute(*ARGV)
GitTracker::Runner.call(*ARGV)
5 changes: 0 additions & 5 deletions git_tracker.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]
spec.platform = Gem::Platform::RUBY

if RUBY_VERSION >= "2.5.0"
spec.add_development_dependency "activesupport", "~> 6.0"
else
spec.add_development_dependency "activesupport", "~> 5.0"
end
spec.add_development_dependency "pry-byebug", "~> 3.9"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "rspec", "~> 3.9"
Expand Down
50 changes: 31 additions & 19 deletions lib/git_tracker/commit_message.rb
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
require "pathname"

module GitTracker
class CommitMessage
def initialize(file)
@file = file
@message = File.read(@file)
end

def mentions_story?(number)
@message =~ /^(?!#).*\[(\w+\s)?(#\d+\s)*##{number}(\s#\d+)*(\s\w+)?\]/io
end

def keyword
@message =~ /\[(fix|fixes|fixed|complete|completes|completed|finish|finishes|finished|deliver|delivers|delivered)\]/io
$1
@file = Pathname(file)
end

def append(text)
body, postscript = parse(@message)
body, postscript = parse(message)
new_message = format_message(body, text, postscript)
File.open(@file, "w") do |f|

file.open("w") do |f|
f.write(new_message)
end

new_message
end

private
def keyword
matches = message.match(KEYWORD_REGEX) || {}
matches[:keyword]
end

def parse(message)
lines = message.split($/)
body = lines.take_while { |line| !line.start_with?("#") }
postscript = lines.slice(body.length..-1)
[body.join("\n"), postscript.join("\n")]
def mentions_story?(number)
/^(?!#).*\[(\w+\s)?(#\d+\s)*##{number}(\s#\d+)*(\s\w+)?\]/i.match?(message)
end

private

KEYWORD_REGEX = /\[(?<keyword>fix|fixes|fixed|complete|completes|completed|finish|finishes|finished|deliver|delivers|delivered)\]/io.freeze
private_constant :KEYWORD_REGEX

attr_reader :file

def format_message(preamble, text, postscript)
<<~MESSAGE
#{preamble.strip}
Expand All @@ -40,5 +41,16 @@ def format_message(preamble, text, postscript)
#{postscript}
MESSAGE
end

def message
@message ||= file.read.freeze
end

def parse(raw_message)
lines = raw_message.split($/)
body = lines.take_while { |line| !line.start_with?("#") }
postscript = lines.slice(body.length..-1)
[body.join("\n"), postscript.join("\n")]
end
end
end
40 changes: 18 additions & 22 deletions lib/git_tracker/hook.rb
Original file line number Diff line number Diff line change
@@ -1,39 +1,35 @@
require "pathname"
require "git_tracker/repository"

module GitTracker
class Hook
attr_reader :hook_file
BODY = <<~HOOK.freeze
#!/usr/bin/env bash

def self.init
init_at(Repository.root)
end
if command -v git-tracker >/dev/null; then
git-tracker prepare-commit-msg "$@"
fi

HOOK

attr_reader :hook_file

def self.init_at(root)
new(root).write
def self.init(at:)
new(at: at).write
end

def initialize(root)
@hook_file = File.join(root, ".git", "hooks", "prepare-commit-msg")
def initialize(at:)
@hook_file = Pathname(at).join(PREPARE_COMMIT_MSG_PATH)
end

def write
File.open(hook_file, "w") do |f|
f.write(hook_body)
hook_file.open("w") do |f|
f.write(BODY)
f.chmod(0o755)
end
end

private

def hook_body
<<~HOOK
#!/usr/bin/env bash

if command -v git-tracker >/dev/null; then
git-tracker prepare-commit-msg "$@"
fi

HOOK
end
PREPARE_COMMIT_MSG_PATH = ".git/hooks/prepare-commit-msg".freeze
private_constant :PREPARE_COMMIT_MSG_PATH
end
end
6 changes: 3 additions & 3 deletions lib/git_tracker/prepare_commit_message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ module GitTracker
class PrepareCommitMessage
attr_reader :file, :source, :commit_sha

def self.run(file, source = nil, commit_sha = nil)
new(file, source, commit_sha).run
def self.call(file, source = nil, commit_sha = nil)
new(file, source, commit_sha).call
end

def initialize(file, source = nil, commit_sha = nil)
Expand All @@ -15,7 +15,7 @@ def initialize(file, source = nil, commit_sha = nil)
@commit_sha = commit_sha
end

def run
def call
exit_when_commit_exists

story = story_number_from_branch
Expand Down
82 changes: 63 additions & 19 deletions lib/git_tracker/runner.rb
Original file line number Diff line number Diff line change
@@ -1,37 +1,81 @@
require "optparse"
require "git_tracker/prepare_commit_message"
require "git_tracker/hook"
require "git_tracker/repository"
require "git_tracker/version"

module GitTracker
module Runner
def self.execute(cmd_arg = "help", *args)
command = cmd_arg.tr("-", "_")
abort("[git_tracker] command: '#{cmd_arg}' does not exist.") unless respond_to?(command)
send(command, *args)
class Runner
def self.call(*args, io: $stdout)
args << "--help" if args.empty?
options = {}

OptionParser.new { |optparse|
optparse.banner = <<~BANNER
git-tracker is a Git hook used during the normal lifecycle of committing,
rebasing, merging, etc… This hook must be initialized into each repository
in which you wish to use it.

usage: git-tracker init
BANNER

optparse.on("-h", "--help", "Prints this help") do
io.puts(optparse)
options[:exit] = true
end

optparse.on("-v", "--version", "Prints the git-tracker version number") do
io.puts("git-tracker #{VERSION}")
options[:exit] = true
end
}.parse!(args)

return if options.fetch(:exit, false)

command, *others = args

new(command: command, arguments: others, options: options).call
end

def self.prepare_commit_msg(*args)
PrepareCommitMessage.run(*args)
def initialize(command:, arguments:, options:)
@command = command
@arguments = arguments
@options = options
end

def self.init
Hook.init
def call
abort("[git_tracker] command: '#{command}' does not exist.") unless sub_command

send(sub_command)
end

def self.install
puts "`git-tracker install` is deprecated. Please use `git-tracker init`"
private

SUB_COMMANDS = {
init: :init,
install: :install,
"prepare-commit-msg": :prepare_commit_msg
}.freeze
private_constant :SUB_COMMANDS

attr_reader :arguments, :command, :options

def init
Hook.init(at: Repository.root)
end

def install
warn("`git-tracker install` is deprecated. Please use `git-tracker init`.")

init
end

def self.help
puts <<~HELP
git-tracker #{VERSION} is installed.
def prepare_commit_msg
PrepareCommitMessage.call(*arguments)
end

Remember, git-tracker is a hook which Git interacts with during its normal
lifecycle of committing, rebasing, merging, etc. You need to initialize this
hook by running `git-tracker init` from each repository in which you wish to
use it. Cheers!
HELP
def sub_command
@sub_command ||= SUB_COMMANDS.fetch(command.intern, false)
end
end
end
Loading