derailed_benchmarks 1.4.3 → 1.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check_changelog.yml +11 -8
- data/.travis.yml +9 -7
- data/Appraisals +16 -16
- data/CHANGELOG.md +29 -1
- data/README.md +14 -4
- data/derailed_benchmarks.gemspec +4 -3
- data/gemfiles/rails_5_1.gemfile +3 -1
- data/gemfiles/rails_5_2.gemfile +3 -3
- data/lib/derailed_benchmarks.rb +2 -1
- data/lib/derailed_benchmarks/core_ext/kernel_require.rb +29 -24
- data/lib/derailed_benchmarks/git/commit.rb +36 -0
- data/lib/derailed_benchmarks/git/in_path.rb +59 -0
- data/lib/derailed_benchmarks/git/switch_project.rb +128 -0
- data/lib/derailed_benchmarks/git_switch_project.rb +1 -0
- data/lib/derailed_benchmarks/load_tasks.rb +11 -4
- data/lib/derailed_benchmarks/require_tree.rb +11 -1
- data/lib/derailed_benchmarks/{stats_in_file.rb → stats_for_file.rb} +8 -2
- data/lib/derailed_benchmarks/stats_from_dir.rb +68 -13
- data/lib/derailed_benchmarks/tasks.rb +34 -63
- data/lib/derailed_benchmarks/version.rb +1 -1
- data/test/derailed_benchmarks/core_ext/kernel_require_test.rb +70 -11
- data/test/derailed_benchmarks/git_switch_project_test.rb +83 -0
- data/test/derailed_benchmarks/require_tree_test.rb +1 -1
- data/test/derailed_benchmarks/stats_from_dir_test.rb +57 -9
- data/test/fixtures/require/autoload_child.rb +5 -0
- data/test/fixtures/require/autoload_parent.rb +8 -0
- data/test/fixtures/require/child_one.rb +1 -1
- data/test/fixtures/require/child_two.rb +1 -1
- data/test/fixtures/require/load_child.rb +3 -0
- data/test/fixtures/require/load_parent.rb +5 -0
- data/test/fixtures/require/parent_one.rb +1 -1
- data/test/integration/tasks_test.rb +43 -5
- data/test/rails_app/config/application.rb +2 -0
- data/test/test_helper.rb +6 -1
- metadata +46 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0f29e3b52bfe2c3c549a9908cf835ec63430afc8d8c733f8ccbf73646b0aa07
|
4
|
+
data.tar.gz: c43b9cc6602884bc33c3e6479b75250564ef4b54f895b87a6d0b5f79f4469704
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 23583aa213f4f2ddb8ead5c9325972bd8fc696289303e16faf34023e5f732288d51cd9d5fe9edb0f37a44c0a1f3ebbe32d74c6355ba85b4f9dd1793e8e3359d5
|
7
|
+
data.tar.gz: f4074b2f83fd12c4b81e4f9714bae62a0ed15e012d2a4a821d2c038fc537b9b511475482e4928d689421ba473cfb91543431866d1cd424cf6db101b792bcfdb8
|
@@ -1,10 +1,13 @@
|
|
1
1
|
name: Check Changelog
|
2
|
-
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
types: [opened, reopened, edited, synchronize]
|
3
6
|
jobs:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
build:
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v1
|
11
|
+
- name: Check that CHANGELOG is touched
|
12
|
+
run: |
|
13
|
+
cat $GITHUB_EVENT_PATH | jq .pull_request.title | grep -i '\[\(\(changelog skip\)\|\(ci skip\)\)\]' || git diff remotes/origin/${{ github.base_ref }} --name-only | grep CHANGELOG.md
|
data/.travis.yml
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.
|
5
|
-
-
|
3
|
+
- 2.2.10
|
4
|
+
- 2.5.8
|
5
|
+
- 2.7.1
|
6
6
|
|
7
7
|
gemfile:
|
8
8
|
- gemfiles/rails_5_1.gemfile
|
9
|
-
- gemfiles/rails_5_2.gemfile
|
10
9
|
- gemfiles/rails_6_0.gemfile
|
11
10
|
- gemfiles/rails_git.gemfile
|
12
11
|
|
13
12
|
before_install:
|
14
|
-
- gem install bundler
|
13
|
+
- gem install bundler -v 1.17.3
|
15
14
|
|
16
|
-
|
15
|
+
jobs:
|
17
16
|
allow_failures:
|
18
|
-
- rvm:
|
17
|
+
- rvm: 2.2.10
|
18
|
+
gemfile: gemfiles/rails_6_0.gemfile
|
19
|
+
- rvm: 2.2.10
|
20
|
+
gemfile: gemfiles/rails_git.gemfile
|
data/Appraisals
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
# -*- mode: ruby -*-
|
2
2
|
# vi: set ft=ruby :
|
3
3
|
|
4
|
-
appraise "rails-3-2" do
|
5
|
-
|
6
|
-
end
|
4
|
+
# appraise "rails-3-2" do
|
5
|
+
# gem "rails", "~> 3.2.0"
|
6
|
+
# end
|
7
7
|
|
8
|
-
appraise "rails-4-0" do
|
9
|
-
|
10
|
-
end
|
8
|
+
# appraise "rails-4-0" do
|
9
|
+
# gem "rails", "~> 4.0.0"
|
10
|
+
# end
|
11
11
|
|
12
|
-
appraise "rails-4-1" do
|
13
|
-
|
14
|
-
end
|
12
|
+
# appraise "rails-4-1" do
|
13
|
+
# gem "rails", "~> 4.1.0"
|
14
|
+
# end
|
15
15
|
|
16
|
-
appraise "rails-4-2" do
|
17
|
-
|
18
|
-
end
|
16
|
+
# appraise "rails-4-2" do
|
17
|
+
# gem "rails", "~> 4.2.0"
|
18
|
+
# end
|
19
19
|
|
20
|
-
appraise "rails-5-0" do
|
21
|
-
|
22
|
-
end
|
20
|
+
# appraise "rails-5-0" do
|
21
|
+
# gem "rails", "~> 5.0.0"
|
22
|
+
# end
|
23
23
|
|
24
24
|
appraise "rails-5-1" do
|
25
|
-
gem "rails", "~> 5.1.
|
25
|
+
gem "rails", "~> 5.1.7"
|
26
26
|
end
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,32 @@
|
|
1
|
-
##
|
1
|
+
## HEAD
|
2
|
+
|
3
|
+
## 1.8.1
|
4
|
+
|
5
|
+
- Derailed now tracks memory use from `load` in addition to `require` (https://github.com/schneems/derailed_benchmarks/pull/178)
|
6
|
+
- Correct logging of unsuccessful curl requests to file (https://github.com/schneems/derailed_benchmarks/pull/172)
|
7
|
+
|
8
|
+
## 1.8.0
|
9
|
+
|
10
|
+
- Ruby 2.2 is now officialy supported and tested (https://github.com/schneems/derailed_benchmarks/pull/177)
|
11
|
+
|
12
|
+
## 1.7.0
|
13
|
+
|
14
|
+
- Add histogram support to `perf:library` (https://github.com/schneems/derailed_benchmarks/pull/169)
|
15
|
+
- Fix bug with `Kernel#require` patch when Zeitwerk is enabled (https://github.com/schneems/derailed_benchmarks/pull/170)
|
16
|
+
|
17
|
+
## 1.6.0
|
18
|
+
|
19
|
+
- Added the `perf:app` command to compare commits within the same application. (https://github.com/schneems/derailed_benchmarks/pull/157)
|
20
|
+
- Allow Rails < 7 and 1.0 <= Thor < 2 (https://github.com/schneems/derailed_benchmarks/pull/168)
|
21
|
+
|
22
|
+
## 1.5.0
|
23
|
+
|
24
|
+
- Test `perf:library` results against 99% confidence interval in addition to 95% (https://github.com/schneems/derailed_benchmarks/pull/165)
|
25
|
+
- Change default, `perf:library` tests do not stop automatically any more (https://github.com/schneems/derailed_benchmarks/pull/164)
|
26
|
+
|
27
|
+
## 1.4.4
|
28
|
+
|
29
|
+
- Fix alignment of deicmals in output (https://github.com/schneems/derailed_benchmarks/pull/161)
|
2
30
|
|
3
31
|
## 1.4.3
|
4
32
|
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ A series of things you can use to benchmark a Rails or Ruby app.
|
|
10
10
|
## Compatibility/Requirements
|
11
11
|
|
12
12
|
This gem has been tested and is known to work with Rails 3.2+ using Ruby
|
13
|
-
2.
|
13
|
+
2.2+. Some commands __may__ work on older versions of Ruby, but not all commands are supported.
|
14
14
|
|
15
15
|
For some benchmarks, not all, you'll need to verify you have a working version of curl on your OS:
|
16
16
|
|
@@ -415,13 +415,23 @@ or point it at your local copy:
|
|
415
415
|
gem 'rails', path: "<path/to/your/local/copy/rails>"
|
416
416
|
```
|
417
417
|
|
418
|
-
To run your
|
418
|
+
To run your tests within the context of your current app/repo:
|
419
|
+
|
420
|
+
```
|
421
|
+
$ bundle exec derailed exec perf:app
|
422
|
+
```
|
423
|
+
|
424
|
+
This will automatically test the two latest commits of your library/current directory.
|
425
|
+
|
426
|
+
If you'd like to test the Rails library instead, make sure that `ENV[DERAILED_PATH_TO_LIBRARY]` is unset.
|
419
427
|
|
420
428
|
```
|
421
429
|
$ bundle exec derailed exec perf:library
|
422
430
|
```
|
423
431
|
|
424
|
-
This will automatically test the two latest commits of Rails
|
432
|
+
This will automatically test the two latest commits of Rails.
|
433
|
+
|
434
|
+
If you would also like to compare against different SHAs you can manually specify them:
|
425
435
|
|
426
436
|
```
|
427
437
|
$ SHAS_TO_TEST="7b4d80cb373e,13d6aa3a7b70" bundle exec derailed exec perf:library
|
@@ -453,7 +463,7 @@ You can use this to test changes in other libraries that aren't rails, you just
|
|
453
463
|
|
454
464
|
> To get the best results before running tests you should close all programs on your laptop, turn on a program to prevent your laptop from going to sleep (or increase your sleep timer). Make sure it's plugged into a power outlet and go grab a cup of coffee. If you do anything on your laptop while this test is running you risk the chance of skewing your results.
|
455
465
|
|
456
|
-
|
466
|
+
As the test is executing, intermediate results will be printed every 50 iterations.
|
457
467
|
|
458
468
|
## Environment Variables
|
459
469
|
|
data/derailed_benchmarks.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
21
|
gem.require_paths = ["lib"]
|
22
22
|
|
23
|
-
gem.required_ruby_version = ">= 2.
|
23
|
+
gem.required_ruby_version = ">= 2.2.0"
|
24
24
|
|
25
25
|
gem.add_dependency "heapy", "~> 0"
|
26
26
|
gem.add_dependency "memory_profiler", "~> 0"
|
@@ -28,12 +28,13 @@ Gem::Specification.new do |gem|
|
|
28
28
|
gem.add_dependency "benchmark-ips", "~> 2"
|
29
29
|
gem.add_dependency "rack", ">= 1"
|
30
30
|
gem.add_dependency "rake", "> 10", "< 14"
|
31
|
-
gem.add_dependency "thor", "
|
31
|
+
gem.add_dependency "thor", ">= 0.19", "< 2"
|
32
32
|
gem.add_dependency "ruby-statistics", ">= 2.1"
|
33
|
+
gem.add_dependency "mini_histogram", ">= 0.2.1"
|
33
34
|
|
34
35
|
gem.add_development_dependency "capybara", "~> 2"
|
35
36
|
gem.add_development_dependency "m"
|
36
|
-
gem.add_development_dependency "rails", "> 3", "<=
|
37
|
+
gem.add_development_dependency "rails", "> 3", "<= 7"
|
37
38
|
gem.add_development_dependency "devise", "> 3", "< 6"
|
38
39
|
gem.add_development_dependency "appraisal", "2.2.0"
|
39
40
|
end
|
data/gemfiles/rails_5_1.gemfile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# BUNDLE_GEMFILE="gemfiles/rails_5_1.gemfile" bundle exec m test/integration/tasks_test.rb:30
|
4
|
+
#
|
3
5
|
# This file was generated by Appraisal
|
4
6
|
|
5
7
|
source "https://rubygems.org"
|
6
8
|
|
7
|
-
gem "rails", "~> 5.1.
|
9
|
+
gem "rails", "~> 5.1.7"
|
8
10
|
|
9
11
|
group :development, :test do
|
10
12
|
gem "sqlite3", platform: [:ruby, :mswin, :mingw]
|
data/gemfiles/rails_5_2.gemfile
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
# This file was generated by Appraisal
|
2
|
+
#
|
3
|
+
# BUNDLE_GEMFILE="gemfiles/rails_5_2.gemfile" bundle exec m test/integration/tasks_test.rb:30
|
4
4
|
|
5
5
|
source "https://rubygems.org"
|
6
6
|
|
7
|
-
gem "rails", "~> 5.2.
|
7
|
+
gem "rails", "~> 5.2.4.4"
|
8
8
|
|
9
9
|
group :development, :test do
|
10
10
|
gem "sqlite3", platform: [:ruby, :mswin, :mingw]
|
data/lib/derailed_benchmarks.rb
CHANGED
@@ -43,8 +43,9 @@ end
|
|
43
43
|
require 'derailed_benchmarks/require_tree'
|
44
44
|
require 'derailed_benchmarks/auth_helper'
|
45
45
|
|
46
|
-
require 'derailed_benchmarks/
|
46
|
+
require 'derailed_benchmarks/stats_for_file'
|
47
47
|
require 'derailed_benchmarks/stats_from_dir'
|
48
|
+
require 'derailed_benchmarks/git/switch_project'
|
48
49
|
|
49
50
|
if DerailedBenchmarks.gem_is_bundled?("devise")
|
50
51
|
DerailedBenchmarks.auth = DerailedBenchmarks::AuthHelpers::Devise.new
|
@@ -11,14 +11,24 @@ ENV['CUT_OFF'] ||= "0.3"
|
|
11
11
|
# Monkey patch kernel to ensure that all `require` calls call the same
|
12
12
|
# method
|
13
13
|
module Kernel
|
14
|
+
REQUIRE_STACK = []
|
14
15
|
|
15
|
-
|
16
|
+
module_function
|
16
17
|
|
17
|
-
|
18
|
-
|
18
|
+
alias_method :original_require, :require
|
19
|
+
alias_method :original_require_relative, :require_relative
|
20
|
+
alias_method(:original_load, :load)
|
21
|
+
|
22
|
+
def load(file, wrap = false)
|
23
|
+
measure_memory_impact(file) do |file|
|
24
|
+
original_load(file)
|
25
|
+
end
|
26
|
+
end
|
19
27
|
|
20
28
|
def require(file)
|
21
|
-
|
29
|
+
measure_memory_impact(file) do |file|
|
30
|
+
original_require(file)
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
34
|
def require_relative(file)
|
@@ -29,10 +39,7 @@ module Kernel
|
|
29
39
|
end
|
30
40
|
end
|
31
41
|
|
32
|
-
|
33
|
-
alias :original_require :require
|
34
|
-
alias :original_require_relative :require_relative
|
35
|
-
end
|
42
|
+
private
|
36
43
|
|
37
44
|
# The core extension we use to measure require time of all requires
|
38
45
|
# When a file is required we create a tree node with its file name.
|
@@ -46,7 +53,7 @@ module Kernel
|
|
46
53
|
# When a require returns we remove it from the require stack so we don't
|
47
54
|
# accidentally push additional children nodes to it. We then store the
|
48
55
|
# memory cost of the require in the tree node.
|
49
|
-
def
|
56
|
+
def measure_memory_impact(file, &block)
|
50
57
|
mem = GetProcessMem.new
|
51
58
|
node = DerailedBenchmarks::RequireTree.new(file)
|
52
59
|
|
@@ -64,31 +71,29 @@ module Kernel
|
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
67
|
-
# Top level node that will store all require information for the entire app
|
68
|
-
TOP_REQUIRE = DerailedBenchmarks::RequireTree.new("TOP")
|
69
|
-
REQUIRE_STACK.push(TOP_REQUIRE)
|
70
|
-
|
71
|
-
Kernel.define_singleton_method(:require) do |file|
|
72
|
-
measure_memory_impact(file) do |file|
|
73
|
-
# "source_annotation_extractor" is deprecated in Rails 6
|
74
|
-
# # if we don't skip the library it leads to a crash
|
75
|
-
# next if file == "rails/source_annotation_extractor" && Rails.version >= '6.0'
|
76
|
-
original_require(file)
|
77
|
-
end
|
78
|
-
end
|
79
74
|
|
75
|
+
# I honestly have no idea why this Object delegation is needed
|
76
|
+
# I keep staring at bootsnap and it doesn't have to do this
|
77
|
+
# is there a bug in their implementation they haven't caught or
|
78
|
+
# am I doing something different?
|
80
79
|
class Object
|
81
80
|
private
|
81
|
+
def load(path, wrap = false)
|
82
|
+
Kernel.load(path, wrap)
|
83
|
+
end
|
82
84
|
|
83
85
|
def require(path)
|
84
86
|
Kernel.require(path)
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
88
|
-
#
|
89
|
-
|
90
|
-
TOP_REQUIRE
|
90
|
+
# Top level node that will store all require information for the entire app
|
91
|
+
TOP_REQUIRE = DerailedBenchmarks::RequireTree.new("TOP")
|
92
|
+
REQUIRE_STACK.push(TOP_REQUIRE)
|
93
|
+
TOP_REQUIRE.cost = GetProcessMem.new.mb
|
94
|
+
|
91
95
|
def TOP_REQUIRE.print_sorted_children(*args)
|
92
96
|
self.cost = GetProcessMem.new.mb - self.cost
|
93
97
|
super
|
94
98
|
end
|
99
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module DerailedBenchmarks
|
2
|
+
# Represents a specific commit in a git repo
|
3
|
+
#
|
4
|
+
# Can be used to get information from the commit or to check it out
|
5
|
+
#
|
6
|
+
# commit = GitCommit.new(path: "path/to/repo", ref: "6e642963acec0ff64af51bd6fba8db3c4176ed6e")
|
7
|
+
# commit.short_sha # => "6e64296"
|
8
|
+
# commit.checkout! # Will check out the current commit at the repo in the path
|
9
|
+
class Git::Commit
|
10
|
+
attr_reader :ref, :description, :time, :short_sha, :log
|
11
|
+
|
12
|
+
def initialize(path: , ref: , log_dir: Pathname.new("/dev/null"))
|
13
|
+
@in_git_path = Git::InPath.new(path)
|
14
|
+
@ref = ref
|
15
|
+
@log = log_dir.join("#{file_safe_ref}.bench.txt")
|
16
|
+
|
17
|
+
Dir.chdir(path) do
|
18
|
+
checkout!
|
19
|
+
@description = @in_git_path.description
|
20
|
+
@short_sha = @in_git_path.short_sha
|
21
|
+
@time = @in_git_path.time
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
alias :desc :description
|
26
|
+
alias :file :log
|
27
|
+
|
28
|
+
def checkout!
|
29
|
+
@in_git_path.checkout!(ref)
|
30
|
+
end
|
31
|
+
|
32
|
+
private def file_safe_ref
|
33
|
+
ref.gsub('/', ':')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module DerailedBenchmarks
|
2
|
+
# A class for running commands in a git directory
|
3
|
+
#
|
4
|
+
# It's faster to check if we're already in that directory instead
|
5
|
+
# of having to `cd` into each time. https://twitter.com/schneems/status/1305196730170961920
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# in_git_path = InGitPath.new(`bundle info heapy --path`.strip)
|
10
|
+
# in_git_path.checkout!("f0f92b06156f2274021aa42f15326da041ee9009")
|
11
|
+
# in_git_path.short_sha # => "f0f92b0"
|
12
|
+
class Git::InPath
|
13
|
+
attr_reader :path
|
14
|
+
|
15
|
+
def initialize(path)
|
16
|
+
@path = path
|
17
|
+
end
|
18
|
+
|
19
|
+
def description
|
20
|
+
run!("git log --oneline --format=%B -n 1 HEAD | head -n 1")
|
21
|
+
end
|
22
|
+
|
23
|
+
def short_sha
|
24
|
+
run!("git rev-parse --short HEAD")
|
25
|
+
end
|
26
|
+
|
27
|
+
def time_stamp_string
|
28
|
+
run!("git log -n 1 --pretty=format:%ci") # https://stackoverflow.com/a/25921837/147390
|
29
|
+
end
|
30
|
+
|
31
|
+
def branch
|
32
|
+
branch = run!("git rev-parse --abbrev-ref HEAD")
|
33
|
+
branch == "HEAD" ? nil : branch
|
34
|
+
end
|
35
|
+
|
36
|
+
def checkout!(ref)
|
37
|
+
run!("git checkout '#{ref}' 2>&1")
|
38
|
+
end
|
39
|
+
|
40
|
+
def time
|
41
|
+
DateTime.parse(time_stamp_string)
|
42
|
+
end
|
43
|
+
|
44
|
+
def run(cmd)
|
45
|
+
if Dir.pwd == path
|
46
|
+
out = `#{cmd}`.strip
|
47
|
+
else
|
48
|
+
out = `cd #{path} && #{cmd}`.strip
|
49
|
+
end
|
50
|
+
out
|
51
|
+
end
|
52
|
+
|
53
|
+
def run!(cmd)
|
54
|
+
out = run(cmd)
|
55
|
+
raise "Error while running #{cmd.inspect}: #{out}" unless $?.success?
|
56
|
+
out
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
module DerailedBenchmarks
|
2
|
+
class Git
|
3
|
+
end
|
4
|
+
end
|
5
|
+
require_relative "in_path.rb"
|
6
|
+
require_relative "commit.rb"
|
7
|
+
|
8
|
+
module DerailedBenchmarks
|
9
|
+
# Wraps two or more git commits in a specific location
|
10
|
+
#
|
11
|
+
# Returns an array of GitCommit objects that can be used to manipulate
|
12
|
+
# and checkout the repo
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
#
|
16
|
+
# `git clone https://sharpstone/default_ruby tmp/default_ruby`
|
17
|
+
#
|
18
|
+
# project = GitSwitchProject.new(path: "tmp/default_ruby")
|
19
|
+
#
|
20
|
+
# By default it will represent the last two commits:
|
21
|
+
#
|
22
|
+
# project.commits.length # => 2
|
23
|
+
#
|
24
|
+
# You can pass in explicit REFs in an array:
|
25
|
+
#
|
26
|
+
# ref_array = ["da748a59340be8b950e7bbbfb32077eb67d70c3c", "9b19275a592f148e2a53b87ead4ccd8c747539c9"]
|
27
|
+
# project = GitSwitchProject.new(path: "tmp/default_ruby", ref_array: ref_array)
|
28
|
+
#
|
29
|
+
# puts project.commits.map(&:ref) == ref_array # => true
|
30
|
+
#
|
31
|
+
#
|
32
|
+
# It knows the current branch or sha:
|
33
|
+
#
|
34
|
+
# `cd tmp/ruby && git checkout -b mybranch`
|
35
|
+
# project.current_branch_or_sha #=> "mybranch"
|
36
|
+
#
|
37
|
+
# It can be used for safely wrapping checkouts to ensure the project returns to it's original branch:
|
38
|
+
#
|
39
|
+
# project.restore_branch_on_return do
|
40
|
+
# project.commits.first.checkout!
|
41
|
+
# project.current_branch_or_sha # => "da748a593"
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# project.current_branch_or_sha # => "mybranch"
|
45
|
+
class Git::SwitchProject
|
46
|
+
attr_reader :commits
|
47
|
+
|
48
|
+
def initialize(path: , ref_array: [], io: STDOUT, log_dir: "/dev/null")
|
49
|
+
@path = Pathname.new(path)
|
50
|
+
|
51
|
+
@in_git_path = Git::InPath.new(@path.expand_path)
|
52
|
+
|
53
|
+
raise "Must be a path with a .git directory '#{@path}'" if !@path.join(".git").exist?
|
54
|
+
@io = io
|
55
|
+
@commits = []
|
56
|
+
log_dir = Pathname(log_dir)
|
57
|
+
|
58
|
+
expand_refs(ref_array).each do |ref|
|
59
|
+
restore_branch_on_return(quiet: true) do
|
60
|
+
@commits << Git::Commit.new(path: @path, ref: ref, log_dir: log_dir)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
if (duplicate = @commits.group_by(&:short_sha).detect {|(k, v)| v.length > 1})
|
65
|
+
raise "Duplicate SHA resolved #{duplicate[0].inspect}: #{duplicate[1].map {|c| "'#{c.ref}' => '#{c.short_sha}'"}.join(", ") } at #{@path}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def current_branch_or_sha
|
70
|
+
branch_or_sha = @in_git_path.branch
|
71
|
+
branch_or_sha ||= @in_git_path.short_sha
|
72
|
+
branch_or_sha
|
73
|
+
end
|
74
|
+
|
75
|
+
def dirty?
|
76
|
+
!clean?
|
77
|
+
end
|
78
|
+
|
79
|
+
# https://stackoverflow.com/a/3879077/147390
|
80
|
+
def clean?
|
81
|
+
@in_git_path.run("git diff-index --quiet HEAD --") && $?.success?
|
82
|
+
end
|
83
|
+
|
84
|
+
private def status(pattern: "*.gemspec")
|
85
|
+
@in_git_path.run("git status #{pattern}")
|
86
|
+
end
|
87
|
+
|
88
|
+
def restore_branch_on_return(quiet: false)
|
89
|
+
if dirty? && status.include?("gemspec")
|
90
|
+
dirty_gemspec = true
|
91
|
+
unless quiet
|
92
|
+
@io.puts "Working tree at #{@path} is dirty, stashing. This will be popped on return"
|
93
|
+
@io.puts "Bundler modifies gemspec files on git install, this is normal"
|
94
|
+
@io.puts "Original status:\n#{status}"
|
95
|
+
end
|
96
|
+
@in_git_path.run!("git stash")
|
97
|
+
end
|
98
|
+
branch_or_sha = self.current_branch_or_sha
|
99
|
+
yield
|
100
|
+
ensure
|
101
|
+
return unless branch_or_sha
|
102
|
+
@io.puts "Resetting git dir of '#{@path.to_s}' to #{branch_or_sha.inspect}" unless quiet
|
103
|
+
|
104
|
+
@in_git_path.checkout!(branch_or_sha)
|
105
|
+
if dirty_gemspec
|
106
|
+
out = @in_git_path.run!("git stash apply 2>&1")
|
107
|
+
@io.puts "Applying stash of '#{@path.to_s}':\n#{out}" unless quiet
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# case ref_array.length
|
112
|
+
# when >= 2
|
113
|
+
# returns original array
|
114
|
+
# when 1
|
115
|
+
# returns the given ref plus the one before it
|
116
|
+
# when 0
|
117
|
+
# returns the most recent 2 refs
|
118
|
+
private def expand_refs(ref_array)
|
119
|
+
return ref_array if ref_array.length >= 2
|
120
|
+
|
121
|
+
@in_git_path.checkout!(ref_array.first) if ref_array.first
|
122
|
+
|
123
|
+
branches_string = @in_git_path.run!("git log --format='%H' -n 2")
|
124
|
+
ref_array = branches_string.split($/)
|
125
|
+
return ref_array
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|