git-lead-time 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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