git-lead-time 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ccefd21676389ce1ced4444101580df84a07abee
4
- data.tar.gz: 9bb2ce0498ad5a2f184af9a1da719a7129fca87e
3
+ metadata.gz: ca4d1acb0055545b95803f223fe78bfe42504173
4
+ data.tar.gz: e09ac3a073fb2796a6569f2d72a2c7f4e97121f6
5
5
  SHA512:
6
- metadata.gz: e63a9c45d4d1e96faeef0dc27c9916cfa8a47349aa0b0c8335ceb2013a67bf17f495a1ae288a7875b31fcadf68356b90120862cf4dace9e0a98b2def1d2f5083
7
- data.tar.gz: 98a1a09d173806a421160d3a1c429d123bd2b3c23f71d0e32027c360612a41e88f535f2bcf9d91a7bda470912f72c277021b8cfe8e92c3d0ceee9173eb02e104
6
+ metadata.gz: cfc91dafae1c5447a630ee6e4658d33bf0265bb4eaa628c655144378beedbe3a0aa692b0529414634992ab5f5362cfbf28d38b673f4903ab11e685629f40b13b
7
+ data.tar.gz: 5fdd4a18bf65d7daebcf75b0514db48eda07c325e03c2bd1bd6e524a5b65aada316e298453f6669b09fcdf3fc68defa8a10de372131271c084fd096d2dedc7ad
@@ -0,0 +1,7 @@
1
+ ## 0.0.2
2
+
3
+ * Take nights and weekends into account when calculating lead time.
4
+
5
+ ## 0.0.1
6
+
7
+ * Initial Release, `git lead-time` shows lead time for last 10 merges.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # git lead-time
1
+ # git lead-time [![Gem Version](https://badge.fury.io/rb/git-lead-time.png)](http://badge.fury.io/rb/git-lead-time) [![Code Climate](https://codeclimate.com/github/aaronjensen/git-lead-time.png)](https://codeclimate.com/github/aaronjensen/git-lead-time) [![Build Status](https://travis-ci.org/aaronjensen/git-lead-time.svg?branch=master)](https://travis-ci.org/aaronjensen/git-lead-time)
2
2
 
3
3
  Show the lead time of branches merged into the current branch. Currently an
4
4
  early proof of concept.
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_dependency "business_time", "~>0.7.2"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.5"
22
24
  spec.add_development_dependency "rake"
23
25
  spec.add_development_dependency "rspec", "~>3.0.0.beta2"
@@ -1,7 +1,14 @@
1
+ require 'business_time'
2
+
1
3
  require_relative 'git_lead_time/lead_time_command'
4
+ require_relative 'git_lead_time/calculator'
2
5
 
3
6
  module GitLeadTime
4
7
  def self.run
5
8
  puts LeadTimeCommand.new.run
6
9
  end
10
+
11
+ def self.calculator
12
+ @calculator ||= Calculator.new
13
+ end
7
14
  end
@@ -0,0 +1,21 @@
1
+ require_relative 'merge_enumerator'
2
+ require_relative 'merge_information'
3
+
4
+ module GitLeadTime
5
+ class Branch
6
+ attr_reader :ref, :merge_information
7
+
8
+ def initialize(ref)
9
+ @ref = ref
10
+ @merge_information = MergeInformation.new(ref)
11
+ end
12
+
13
+ def each_merge
14
+ return to_enum(__method__) unless block_given?
15
+
16
+ MergeEnumerator.new(ref).each do |merge|
17
+ yield merge_information.info_for(merge)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module GitLeadTime
2
+ class Calculator
3
+ def lead_time(start_date:, end_date:)
4
+ start_date.business_time_until(end_date) / 60 / 60 / 8
5
+ end
6
+ end
7
+ end
@@ -9,7 +9,7 @@ module GitLeadTime
9
9
 
10
10
  def first_commit(ref)
11
11
  first_commit = ref
12
- first_parents(ref).each do |parent|
12
+ first_parents(ref) do |parent|
13
13
  break if target_refs.include? parent
14
14
  first_commit = parent
15
15
  end
@@ -0,0 +1,13 @@
1
+ module GitLeadTime
2
+ module Git
3
+ def self.status(ref, *fields)
4
+ format = {
5
+ subject: "%s",
6
+ abbreviated_hash: "%h",
7
+ date: "%cd",
8
+ }.values_at(*fields).join("\n")
9
+
10
+ `git show -s --format='#{format}' #{ref}`.lines.map(&:strip)
11
+ end
12
+ end
13
+ end
@@ -1,62 +1,20 @@
1
1
  require 'time'
2
- require_relative 'first_commit_finder'
2
+ require_relative 'branch'
3
+ require_relative 'lead_time_format'
3
4
 
4
5
  module GitLeadTime
5
6
  class LeadTimeCommand
6
- attr_reader :first_commit_finder
7
+ attr_reader :branch, :ref
7
8
  def initialize(ref="HEAD")
8
- @first_commit_finder = FirstCommitFinder.new(ref)
9
+ @branch = Branch.new(ref)
9
10
  end
10
11
 
11
12
  def run
12
- merges.map { |merge| format_merge(merge) }
13
- end
14
-
15
- def merges
16
- `git rev-list --merges --first-parent HEAD | head -10`.lines.map(&:chomp)
13
+ branch.each_merge.map { |merge| format_merge(merge) }
17
14
  end
18
15
 
19
16
  def format_merge(merge)
20
- Format.new(Merge.new(merge, first_commit_finder)).to_s
21
- end
22
-
23
- class Merge
24
- attr_reader *%i[merge_commit first_commit message end_date start_date]
25
-
26
- def initialize(sha, first_commit_finder)
27
- first_sha = first_commit_finder.first_commit("#{sha}^2")
28
- @merge_commit, @message, @end_date = status("%h\n%s\n%cd", sha)
29
- @first_commit, @start_date = status("%h\n%cd", first_sha)
30
- end
31
-
32
- def status(format, ref)
33
- `git show -s --format='#{format}' #{ref}`.lines.map(&:strip)
34
- end
35
-
36
- def lead_time
37
- (Time.parse(end_date) - start_date) / 60 / 60 / 24
38
- end
39
-
40
- def start_date
41
- Time.parse `git show -s --format=%cd #{first_commit}`
42
- end
43
- end
44
-
45
- class Format
46
- attr_reader :merge
47
-
48
- def initialize(merge)
49
- @merge = merge
50
- end
51
-
52
- def to_s
53
- "#{format_lead_time(merge.lead_time)} #{merge.first_commit}..#{merge.merge_commit} #{merge.message}"
54
- end
55
-
56
- def format_lead_time(lead_time)
57
- time = ("%5.1f" % lead_time)
58
- "#{time} day#{"s" unless time == "1.0"}"
59
- end
17
+ LeadTimeFormat.new(merge).to_s
60
18
  end
61
19
  end
62
20
  end
@@ -0,0 +1,18 @@
1
+ module GitLeadTime
2
+ class LeadTimeFormat
3
+ attr_reader :merge
4
+
5
+ def initialize(merge)
6
+ @merge = merge
7
+ end
8
+
9
+ def to_s
10
+ "#{format_lead_time(merge.lead_time)} #{merge.first_commit}..#{merge.merge_commit} #{merge.message}"
11
+ end
12
+
13
+ def format_lead_time(lead_time)
14
+ time = ("%5.1f" % lead_time)
15
+ "#{time} day#{"s" unless time == "1.0"}"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'git'
2
+
3
+ module GitLeadTime
4
+ class Merge
5
+ attr_reader *%i[
6
+ merge_commit first_commit message end_date start_date calculator
7
+ ]
8
+
9
+ def initialize(first_sha:, merge_sha:, calculator: GitLeadTime.calculator)
10
+ @calculator = calculator
11
+
12
+ @merge_commit, @message, @end_date =
13
+ Git.status(merge_sha, :abbreviated_hash, :subject, :date)
14
+ @first_commit, @start_date =
15
+ Git.status(first_sha, :abbreviated_hash, :date)
16
+
17
+ @end_date = Time.parse(@end_date)
18
+ @start_date = Time.parse(@start_date)
19
+ end
20
+
21
+ def lead_time
22
+ calculator.lead_time(start_date: start_date, end_date: end_date)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module GitLeadTime
2
+ class MergeEnumerator
3
+ include Enumerable
4
+
5
+ attr_reader :ref
6
+ def initialize(ref = "HEAD")
7
+ @ref = ref
8
+ end
9
+
10
+ def each
11
+ `git rev-list --merges --first-parent HEAD | head -10`.each_line do |rev|
12
+ yield rev.strip
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'first_commit_finder'
2
+ require_relative 'merge'
3
+
4
+ module GitLeadTime
5
+ class MergeInformation
6
+ attr_reader :first_commit_finder
7
+
8
+ def initialize(ref, first_commit_finder: FirstCommitFinder.new(ref))
9
+ @first_commit_finder = first_commit_finder
10
+ end
11
+
12
+ def info_for(merge_sha)
13
+ # TODO: deal w/ octopus merges
14
+ first_sha = first_commit_finder.first_commit("#{merge_sha}^2")
15
+ Merge.new(first_sha: first_sha, merge_sha: merge_sha)
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module GitLeadTime
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -10,7 +10,7 @@ describe "git lead-time", :git do
10
10
  git :checkout, "master"
11
11
  merge_commit = git_merge "topic", on("thursday 9am")
12
12
  # Example: [master 160a500] Merge branch 'topic'
13
- sha, message = merge_commit.chomp.match(/^\[\S+ ([^\]]+)\] (.*)$/)[1,2]
13
+ sha, message = parse_merge_result(merge_commit)
14
14
 
15
15
  git "log", "--graph"
16
16
  output = git "lead-time"
@@ -18,4 +18,26 @@ describe "git lead-time", :git do
18
18
  " 2.0 days #{first_commit}..#{sha} #{message}"
19
19
  ]
20
20
  end
21
+
22
+ it "does not count weekends in lead time" do
23
+ git_commit "Initial commit", on("last thursday 9am")
24
+ git :checkout, "-b", "topic"
25
+ git_commit "Topic commit A", on("last friday 9am")
26
+ first_commit = git_head(:abbreviated)
27
+ git_commit "Topic commit B", on("last friday 10am")
28
+ git :checkout, "master"
29
+ merge_commit = git_merge "topic", on("monday 9am")
30
+ # Example: [master 160a500] Merge branch 'topic'
31
+ sha, message = parse_merge_result(merge_commit)
32
+
33
+ git "log", "--graph"
34
+ output = git "lead-time"
35
+ expect(output.lines.map(&:chomp)).to eq [
36
+ " 1.0 days #{first_commit}..#{sha} #{message}"
37
+ ]
38
+ end
39
+
40
+ def parse_merge_result(result)
41
+ result.chomp.match(/^\[\S+ ([^\]]+)\] (.*)$/)[1,2]
42
+ end
21
43
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'git_lead_time/calculator'
3
+
4
+ module GitLeadTime
5
+ describe Calculator, "#lead_time" do
6
+ subject(:calculator) { Calculator.new }
7
+
8
+ it "should calculate the number of days between two times" do
9
+ lead_time = calculator.lead_time(
10
+ start_date: date("monday 9am"),
11
+ end_date: date("tuesday 9am")
12
+ )
13
+
14
+ expect(lead_time).to eq 1.0
15
+ end
16
+
17
+ it "should not count weekends" do
18
+ lead_time = calculator.lead_time(
19
+ start_date: date("last friday 9am"),
20
+ end_date: date("monday 9am")
21
+ )
22
+
23
+ expect(lead_time).to eq 1.0
24
+ end
25
+
26
+ it "should not count evenings" do
27
+ lead_time = calculator.lead_time(
28
+ start_date: date("monday 5pm"),
29
+ end_date: date("tuesday 10am")
30
+ )
31
+
32
+ expect(lead_time).to eq 1.0 / 8.0
33
+ end
34
+ end
35
+ end
@@ -4,19 +4,38 @@ require 'git_lead_time/first_commit_finder'
4
4
  module GitLeadTime
5
5
  describe FirstCommitFinder, :git do
6
6
  it "finds the first commit of a new branch" do
7
- git_commit "Initial commit"
7
+ # Make a nasty graph complete with back merge
8
+ # * ca4080f - (HEAD, master) Merge branch 'topic' (0 seconds ago) <Aaron Jensen>
9
+ # |\
10
+ # | * d6b8382 - (topic) Topic C (0 seconds ago) <Aaron Jensen>
11
+ # | * d8d405a - Merge branch 'master' into topic (0 seconds ago) <Aaron Jensen>
12
+ # | |\
13
+ # | |/
14
+ # |/|
15
+ # * | 70a1894 - Master B (0 seconds ago) <Aaron Jensen>
16
+ # | * 627ed43 - Topic B (0 seconds ago) <Aaron Jensen>
17
+ # | * dd3be46 - Topic A (0 seconds ago) <Aaron Jensen>
18
+ # |/
19
+ # * bfd3a2b - Master A (0 seconds ago) <Aaron Jensen>
20
+ git_commit "Master A"
8
21
 
9
22
  git "checkout", "-b", "topic"
10
23
  git_commit "Topic A"
11
24
  first_commit = git_head
12
25
  git_commit "Topic B"
26
+
27
+ git "checkout", "master"
28
+ git_commit "Master B"
29
+
30
+ git "checkout", "topic"
31
+ git_merge "master"
32
+
13
33
  git_commit "Topic C"
14
34
 
15
35
  git "checkout", "master"
16
36
  git_merge "topic"
17
37
  git "branch", "-D", "topic"
18
38
 
19
-
20
39
  finder = FirstCommitFinder.new("master")
21
40
  expect(finder.first_commit("master^2")).to eq(first_commit)
22
41
  end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'git_lead_time/git'
3
+
4
+ module GitLeadTime
5
+ describe Git, ".status", :git do
6
+ before do
7
+ commit = git_commit "My subject", on("monday")
8
+ @hash = commit[/ ([0-9a-f]*)\]/, 1]
9
+ end
10
+
11
+ it "should return the subject" do
12
+ subject = Git.status("HEAD", :subject).first
13
+ expect(subject).to eq "My subject"
14
+ end
15
+
16
+ it "should return the date" do
17
+ date = Git.status("HEAD", :date).first
18
+ expect(date).to eq git_date("monday")
19
+ end
20
+
21
+ it "should return the abbreviated hash" do
22
+ hash = Git.status("HEAD", :abbreviated_hash).first
23
+ expect(hash).to eq @hash
24
+ end
25
+
26
+ it "should return everything at once" do
27
+ hash, subject, date =
28
+ Git.status("HEAD", :abbreviated_hash, :subject, :date)
29
+
30
+ expect(hash).to eq @hash
31
+ expect(subject).to eq "My subject"
32
+ expect(date).to eq git_date("monday")
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'git_lead_time/merge_enumerator'
3
+
4
+ module GitLeadTime
5
+ describe MergeEnumerator, :git do
6
+ it "enumerates merges" do
7
+ git_commit "Initial commit"
8
+ merges = 3.times.map do
9
+ git :checkout, "-b", "topic"
10
+ git_commit "Topic commit"
11
+ git :checkout, "master"
12
+ git_merge "topic"
13
+ git :branch, "-D", "topic"
14
+ git_head
15
+ end
16
+
17
+ enum = MergeEnumerator.new("master")
18
+
19
+ expect(enum.to_a).to eq merges.reverse
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require 'git_lead_time/merge'
3
+
4
+ module GitLeadTime
5
+ describe Merge do
6
+ let(:calculator) { instance_double "GitLeadTime::Calculator" }
7
+ subject(:merge) { Merge.new(first_sha: "aa", merge_sha: "bb", calculator: calculator) }
8
+
9
+ before do
10
+ allow(Git).to receive(:status).with("aa", :abbreviated_hash, :date) {
11
+ [ "a", git_date("monday") ] }
12
+ allow(Git).to receive(:status).with("bb", :abbreviated_hash, :subject, :date) {
13
+ [ "b", "subject", git_date("tuesday") ] }
14
+ end
15
+
16
+ it "should get info from git" do
17
+ expect(merge.message).to eq "subject"
18
+ expect(merge.start_date).to eq date("monday")
19
+ expect(merge.end_date).to eq date("tuesday")
20
+ expect(merge.first_commit).to eq "a"
21
+ expect(merge.merge_commit).to eq "b"
22
+ end
23
+
24
+ it "should calculate lead time" do
25
+ allow(calculator).to receive(:lead_time)
26
+ .with(start_date: date("monday"), end_date: date("tuesday")) { "5" }
27
+
28
+ expect(merge.lead_time).to eq "5"
29
+ end
30
+ end
31
+ end
@@ -1,3 +1,6 @@
1
+ require 'business_time'
2
+ I18n.config.enforce_available_locales = true
3
+
1
4
  Dir['./spec/support/**/*.rb'].map {|f| require f}
2
5
  ENV["PATH"] = "#{File.expand_path("../../bin", __FILE__)}:#{ENV['PATH']}"
3
6
 
@@ -0,0 +1,15 @@
1
+ module Support
2
+ module Dates
3
+ def date(str)
4
+ Chronic.parse(str, now: Time.local(2014, 2, 1))
5
+ end
6
+
7
+ def git_date(str)
8
+ date(str).strftime("%a %b %-e %k:%M:%S %Y %z")
9
+ end
10
+ end
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.include Support::Dates
15
+ end
@@ -25,11 +25,11 @@ module Support
25
25
  end
26
26
 
27
27
  def git_merge(branch, date: :now)
28
- git :merge, "--no-ff", "--no-commit", "topic", date: date
28
+ git :merge, "--no-ff", "--no-commit", branch, date: date
29
29
  git_commit :no_edit, date: date
30
30
  end
31
31
 
32
- def git_head(abbreviated)
32
+ def git_head(abbreviated=false)
33
33
  if abbreviated
34
34
  git("show", "-s", "--format=%h").chomp
35
35
  else
@@ -52,8 +52,8 @@ module Support
52
52
  ENV["GIT_COMMITTER_DATE"] = nil
53
53
  end
54
54
 
55
- def on(date)
56
- { date: Chronic.parse(date, now: Time.local(2014, 2, 1)).strftime("%a, %b %d %k:%M %Y %z") }
55
+ def on(str)
56
+ { date: git_date(str) }
57
57
  end
58
58
  end
59
59
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-lead-time
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Jensen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-27 00:00:00.000000000 Z
11
+ date: 2014-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: business_time
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.7.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.7.2
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -76,6 +90,7 @@ extra_rdoc_files: []
76
90
  files:
77
91
  - ".gitignore"
78
92
  - ".travis.yml"
93
+ - CHANGELOG.md
79
94
  - Gemfile
80
95
  - LICENSE.txt
81
96
  - README.md
@@ -84,12 +99,24 @@ files:
84
99
  - git-lead-time.gemspec
85
100
  - lib/git-lead-time.rb
86
101
  - lib/git_lead_time.rb
102
+ - lib/git_lead_time/branch.rb
103
+ - lib/git_lead_time/calculator.rb
87
104
  - lib/git_lead_time/first_commit_finder.rb
105
+ - lib/git_lead_time/git.rb
88
106
  - lib/git_lead_time/lead_time_command.rb
107
+ - lib/git_lead_time/lead_time_format.rb
108
+ - lib/git_lead_time/merge.rb
109
+ - lib/git_lead_time/merge_enumerator.rb
110
+ - lib/git_lead_time/merge_information.rb
89
111
  - lib/git_lead_time/version.rb
90
112
  - spec/features/lead_time_spec.rb
113
+ - spec/lib/git_lead_time/calculator_spec.rb
91
114
  - spec/lib/git_lead_time/first_commit_finder_spec.rb
115
+ - spec/lib/git_lead_time/git_spec.rb
116
+ - spec/lib/git_lead_time/merge_enumerator_spec.rb
117
+ - spec/lib/git_lead_time/merge_spec.rb
92
118
  - spec/spec_helper.rb
119
+ - spec/support/dates.rb
93
120
  - spec/support/git.rb
94
121
  homepage: ''
95
122
  licenses:
@@ -117,6 +144,11 @@ specification_version: 4
117
144
  summary: Show the lead time of branches merged into the current branch.
118
145
  test_files:
119
146
  - spec/features/lead_time_spec.rb
147
+ - spec/lib/git_lead_time/calculator_spec.rb
120
148
  - spec/lib/git_lead_time/first_commit_finder_spec.rb
149
+ - spec/lib/git_lead_time/git_spec.rb
150
+ - spec/lib/git_lead_time/merge_enumerator_spec.rb
151
+ - spec/lib/git_lead_time/merge_spec.rb
121
152
  - spec/spec_helper.rb
153
+ - spec/support/dates.rb
122
154
  - spec/support/git.rb