git_tracker 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ tags
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ -I spec/support
3
+ -r rspec-spies
4
+ -r pry
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in git_tracker.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Steven Harman
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # GitTracker
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'git_tracker'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install git_tracker
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/bin/git-tracker ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'git_tracker'
4
+ GitTracker.execute(*ARGV)
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/git_tracker/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "git_tracker"
6
+ gem.version = GitTracker::VERSION
7
+ gem.authors = ["Steven Harman"]
8
+ gem.email = ["steveharman@gmail.com"]
9
+ gem.homepage = "https://github.com/highgroove/git_tracker"
10
+ gem.summary = %q{Teaching Git about Pivotal Tracker.}
11
+ gem.description = <<-EOF
12
+ Some simple tricks that make working with Pivotal Tracker even
13
+ better... and easier... um, besier!
14
+ EOF
15
+
16
+ gem.add_development_dependency "rspec", "~> 2.0"
17
+ gem.add_development_dependency "rspec-spies", "~> 2.0"
18
+ gem.add_development_dependency "pry", "~> 0.9.8"
19
+
20
+ gem.files = `git ls-files`.split("\n")
21
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ gem.executables = %w(git-tracker)
23
+ gem.require_paths = ["lib"]
24
+ end
@@ -0,0 +1,14 @@
1
+ require "git_tracker/version"
2
+ require "git_tracker/prepare_commit_message"
3
+
4
+ module GitTracker
5
+ def self.execute(hook, *args)
6
+ hook_name = hook.gsub(/-/, '_')
7
+ abort("[git_tracker] hook: '#{hook}' does not exist.") unless respond_to?(hook_name)
8
+ send(hook_name, *args)
9
+ end
10
+
11
+ def self.prepare_commit_msg(*args)
12
+ PrepareCommitMessage.run(*args)
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ module GitTracker
2
+ module Branch
3
+ def self.story_number
4
+ current[/#(?<number>\d+)/, :number]
5
+ end
6
+
7
+ def self.current
8
+ branch_path = `git symbolic-ref HEAD`
9
+ branch_path[%r{refs/heads/(?<name>.+)}, :name] || ''
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ module GitTracker
2
+ class CommitMessage
3
+
4
+ def initialize(file)
5
+ @file = file
6
+ @message = File.read(@file)
7
+ end
8
+
9
+ def mentions_story?(number)
10
+ @message =~ /^(?!#).*\[(\w+\s)?(#\d+\s)*##{number}(\s#\d+)*(\s\w+)?\]/io
11
+ end
12
+
13
+ def append(text)
14
+ body, postscript = parse(@message)
15
+ new_message = format_message(body, text, postscript)
16
+ File.open(@file, 'w') do |f|
17
+ f.write(new_message)
18
+ end
19
+ new_message
20
+ end
21
+
22
+ private
23
+
24
+ def parse(message)
25
+ lines = message.split($/)
26
+ body = lines.take_while { |line| !line.start_with?("#") }
27
+ postscript = lines.slice(body.length..-1)
28
+ [body.join("\n"), postscript.join("\n")]
29
+ end
30
+
31
+ def format_message(preamble, text, postscript)
32
+ return <<-MESSAGE
33
+ #{preamble.strip}
34
+
35
+ #{text}
36
+ #{postscript}
37
+ MESSAGE
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,34 @@
1
+ require 'git_tracker/branch'
2
+ require 'git_tracker/commit_message'
3
+
4
+ module GitTracker
5
+ class PrepareCommitMessage
6
+ attr_reader :file, :source, :commit_sha
7
+
8
+ def self.run(file, source=nil, commit_sha=nil)
9
+ new(file, source, commit_sha).run
10
+ end
11
+
12
+ def initialize(file, source=nil, commit_sha=nil)
13
+ @file = file
14
+ @source = source
15
+ @commit_sha = commit_sha
16
+ end
17
+
18
+ def run
19
+ story = story_number_from_branch
20
+
21
+ message = CommitMessage.new(file)
22
+ exit if message.mentions_story?(story)
23
+ message.append("[##{story}]")
24
+ end
25
+
26
+ private
27
+
28
+ def story_number_from_branch
29
+ story = Branch.story_number
30
+ exit unless story
31
+ story
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module GitTracker
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env bash
2
+
3
+ git-tracker prepare-commit-msg "$@"
4
+
@@ -0,0 +1,38 @@
1
+ require 'git_tracker/branch'
2
+
3
+ describe GitTracker::Branch do
4
+ subject { described_class }
5
+
6
+ def stub_branch(ref)
7
+ subject.stub('`') { ref }
8
+ end
9
+
10
+ context "Current branch has a story number" do
11
+ before do
12
+ stub_branch('refs/heads/a_very_descriptive_name_#8675309')
13
+ end
14
+
15
+ it "shells out to git, looking for the current HEAD" do
16
+ subject.should_receive('`').with('git symbolic-ref HEAD')
17
+ subject.story_number
18
+ end
19
+
20
+ it "finds the story" do
21
+ subject.story_number.should == '8675309'
22
+ end
23
+ end
24
+
25
+ context "The current branch doesn't have a story number" do
26
+ it "finds no story" do
27
+ stub_branch('refs/heads/a_very_descriptive_name_without_a_#number')
28
+ subject.story_number.should_not be
29
+ end
30
+ end
31
+
32
+ context "Not on a branch (HEAD doesn't exist)" do
33
+ it "finds no story" do
34
+ stub_branch('')
35
+ subject.story_number.should_not be
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,128 @@
1
+ require 'git_tracker/commit_message'
2
+ require 'commit_message_helper'
3
+
4
+ describe GitTracker::CommitMessage do
5
+ include CommitMessageHelper
6
+
7
+ subject { described_class.new(file) }
8
+ let(:file) { "COMMIT_EDITMSG" }
9
+
10
+ it "requires path to the temporary commit message file" do
11
+ -> { GitTracker::CommitMessage.new }.should raise_error ArgumentError
12
+ end
13
+
14
+ describe "#mentions_story?" do
15
+ def stub_commit_message(story_text)
16
+ File.stub(:read).with(file) { example_commit_message(story_text) }
17
+ end
18
+
19
+ context "commit message contains the special Pivotal Tracker story syntax" do
20
+ it "allows just the number" do
21
+ stub_commit_message("[#8675309]")
22
+ subject.should be_mentions_story("8675309")
23
+ end
24
+
25
+ it "allows multiple numbers" do
26
+ stub_commit_message("[#99 #777 #8675309 #111222]")
27
+ subject.should be_mentions_story("99")
28
+ subject.should be_mentions_story("777")
29
+ subject.should be_mentions_story("8675309")
30
+ subject.should be_mentions_story("111222")
31
+ end
32
+
33
+ it "allows state change before number" do
34
+ stub_commit_message("[Fixes #8675309]")
35
+ subject.should be_mentions_story("8675309")
36
+ end
37
+
38
+ it "allows state change after the number" do
39
+ stub_commit_message("[#8675309 Delivered]")
40
+ subject.should be_mentions_story("8675309")
41
+ end
42
+
43
+ it "allows surrounding text" do
44
+ stub_commit_message("derp de #herp [Fixes #8675309] de herp-ity derp")
45
+ subject.should be_mentions_story("8675309")
46
+ end
47
+ end
48
+
49
+ context "commit message doesn't contain the special Pivotal Tracker story syntax" do
50
+ it "requires brackets" do
51
+ stub_commit_message("#8675309")
52
+ subject.should_not be_mentions_story("8675309")
53
+ end
54
+
55
+ it "requires a pound sign" do
56
+ stub_commit_message("[8675309]")
57
+ subject.should_not be_mentions_story("8675309")
58
+ end
59
+
60
+ it "doesn't allow the bare number" do
61
+ stub_commit_message("8675309")
62
+ subject.should_not be_mentions_story("8675309")
63
+ end
64
+
65
+ it "doesn't allow multiple state changes" do
66
+ stub_commit_message("[Fixes Deploys #8675309]")
67
+ subject.should_not be_mentions_story("8675309")
68
+ end
69
+
70
+ it "doesn't allow comments" do
71
+ stub_commit_message("#[#8675309]")
72
+ subject.should_not be_mentions_story("8675309")
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#append" do
78
+ let(:fake_file) { GitTracker::FakeFile.new }
79
+ before do
80
+ File.stub(:open).and_yield(fake_file)
81
+ end
82
+ def stub_original_commit_message(message)
83
+ File.stub(:read) { message }
84
+ end
85
+
86
+ it "handles no existing message" do
87
+ stub_original_commit_message("\n\n# some other comments\n")
88
+ subject.append("[#8675309]")
89
+
90
+ fake_file.content.should ==
91
+ <<-COMMIT_MESSAGE
92
+
93
+
94
+ [#8675309]
95
+ # some other comments
96
+ COMMIT_MESSAGE
97
+ end
98
+
99
+ it "preserves existing messages" do
100
+ stub_original_commit_message("A first line\n\nWith more here\n# other comments\n")
101
+ subject.append("[#8675309]")
102
+
103
+ fake_file.content.should == <<-COMMIT_MESSAGE
104
+ A first line
105
+
106
+ With more here
107
+
108
+ [#8675309]
109
+ # other comments
110
+ COMMIT_MESSAGE
111
+ end
112
+
113
+ it "preserves line breaks in comments" do
114
+ stub_original_commit_message("# comment #1\n# comment B\n# comment III")
115
+ subject.append("[#8675309]")
116
+
117
+ fake_file.content.should == <<-COMMIT_MESSAGE
118
+
119
+
120
+ [#8675309]
121
+ # comment #1
122
+ # comment B
123
+ # comment III
124
+ COMMIT_MESSAGE
125
+ end
126
+ end
127
+
128
+ end
@@ -0,0 +1,77 @@
1
+ require 'git_tracker/prepare_commit_message'
2
+
3
+ describe GitTracker::PrepareCommitMessage do
4
+ subject { GitTracker::PrepareCommitMessage }
5
+
6
+ describe '.run' do
7
+ let(:hook) { stub("PrepareCommitMessage") }
8
+ before do
9
+ subject.stub(:new) { hook }
10
+ end
11
+
12
+ it "runs the hook" do
13
+ hook.should_receive(:run)
14
+ subject.run("FILE1", "hook_source", "sha1234")
15
+ end
16
+ end
17
+
18
+ describe ".new" do
19
+
20
+ it "requires the name of the commit message file" do
21
+ lambda { subject.new }.should raise_error(ArgumentError)
22
+ end
23
+
24
+ it "remembers the name of the commit message file" do
25
+ subject.new("FILE1").file.should == "FILE1"
26
+ end
27
+
28
+ it "optionally accepts a message source" do
29
+ hook = subject.new("FILE1", "merge").source.should == "merge"
30
+ end
31
+
32
+ it "optionally accepts the SHA-1 of a commit" do
33
+ hook = subject.new("FILE1", "commit", "abc1234").commit_sha.should == "abc1234"
34
+ end
35
+ end
36
+
37
+ describe '#run' do
38
+ let(:hook) { GitTracker::PrepareCommitMessage.new("FILE1") }
39
+ before do
40
+ GitTracker::Branch.stub(:story_number) { story }
41
+ end
42
+
43
+ context "branch name without a Pivotal Tracker story number" do
44
+ let(:story) { nil }
45
+
46
+ it "exits without updating the commit message" do
47
+ lambda { hook.run }.should raise_exception(SystemExit)
48
+ GitTracker::CommitMessage.should_not have_received(:append)
49
+ end
50
+ end
51
+
52
+ context "branch name with a Pivotal Tracker story number" do
53
+ let(:story) { "8675309" }
54
+ let(:commit_message) { stub("CommitMessage", mentions_story?: false) }
55
+ before do
56
+ GitTracker::CommitMessage.stub(:new) { commit_message }
57
+ end
58
+
59
+ it "appends the number to the commit message" do
60
+ commit_message.should_receive(:append).with("[#8675309]")
61
+ hook.run
62
+ end
63
+
64
+ context "number already mentioned in the commit message" do
65
+ before do
66
+ commit_message.stub(:mentions_story?).with("8675309") { true }
67
+ end
68
+
69
+ it "exits without updating the commit message" do
70
+ lambda { hook.run }.should raise_exception(SystemExit)
71
+ GitTracker::CommitMessage.should_not have_received(:append)
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,31 @@
1
+ require 'git_tracker'
2
+
3
+ describe GitTracker do
4
+ subject { described_class }
5
+ let(:args) { ["a_file", "the_source", "sha1234"] }
6
+
7
+ describe ".execute" do
8
+ before do
9
+ subject.stub(:prepare_commit_msg) { true }
10
+ end
11
+
12
+ it "runs the hook, passing the args" do
13
+ subject.should_receive(:prepare_commit_msg).with(*args) { true }
14
+ subject.execute('prepare-commit-msg', *args)
15
+ end
16
+
17
+ # TODO: stop the abort from writing to stderr during tests?
18
+ it "doesn't run hooks we don't know about" do
19
+ lambda { subject.execute('non-existent-hook', *args) }.
20
+ should raise_error SystemExit, "[git_tracker] hook: 'non-existent-hook' does not exist."
21
+ end
22
+ end
23
+
24
+ describe ".prepare_commit_msg" do
25
+ it "runs the hook, passing the args" do
26
+ GitTracker::PrepareCommitMessage.should_receive(:run).with(*args) { true }
27
+ subject.prepare_commit_msg(*args)
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,29 @@
1
+ module CommitMessageHelper
2
+
3
+ def example_commit_message(pattern_to_match)
4
+ return <<-EXAMPLE
5
+ Got Jenny's number, gonna' make her mine!
6
+
7
+ #{pattern_to_match}
8
+ # Please enter the commit message for your changes. Lines starting
9
+ # with '#' will be ignored, and an empty message aborts the commit.
10
+ # On branch get_jennys_number_#8675309
11
+ # Changes to be committed:
12
+ # (use "git reset HEAD <file>..." to unstage)
13
+ #
14
+ # new file: fake_file.rb
15
+ #
16
+
17
+ EXAMPLE
18
+ end
19
+ end
20
+
21
+ module GitTracker
22
+ class FakeFile
23
+ attr_reader :content
24
+
25
+ def write(content)
26
+ @content = content
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git_tracker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Steven Harman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70146115751180 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70146115751180
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec-spies
27
+ requirement: &70146115750200 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70146115750200
36
+ - !ruby/object:Gem::Dependency
37
+ name: pry
38
+ requirement: &70146115749520 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.8
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70146115749520
47
+ description: ! " Some simple tricks that make working with Pivotal Tracker even\n
48
+ \ better... and easier... um, besier!\n"
49
+ email:
50
+ - steveharman@gmail.com
51
+ executables:
52
+ - git-tracker
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - .rspec
58
+ - Gemfile
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - bin/git-tracker
63
+ - git_tracker.gemspec
64
+ - lib/git_tracker.rb
65
+ - lib/git_tracker/branch.rb
66
+ - lib/git_tracker/commit_message.rb
67
+ - lib/git_tracker/prepare_commit_message.rb
68
+ - lib/git_tracker/version.rb
69
+ - prepare-commit-msg.example
70
+ - spec/git_tracker/branch_spec.rb
71
+ - spec/git_tracker/commit_message_spec.rb
72
+ - spec/git_tracker/prepare_commit_message_spec.rb
73
+ - spec/git_tracker_spec.rb
74
+ - spec/support/commit_message_helper.rb
75
+ homepage: https://github.com/highgroove/git_tracker
76
+ licenses: []
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 1.8.17
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: Teaching Git about Pivotal Tracker.
99
+ test_files:
100
+ - spec/git_tracker/branch_spec.rb
101
+ - spec/git_tracker/commit_message_spec.rb
102
+ - spec/git_tracker/prepare_commit_message_spec.rb
103
+ - spec/git_tracker_spec.rb
104
+ - spec/support/commit_message_helper.rb