git_miner 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0aa67e6c72b1527778f786fb604807437789516188c2b0aac1d6520701571762
4
+ data.tar.gz: 9323e1fcd8d7ab220b10ef465baf8a158252b7bf6f317366707f7954a69f11e3
5
+ SHA512:
6
+ metadata.gz: 6d6e3c36b03808d24f4e8ae8dafd7cec12da24d586cabc637d667ed76a5c9c446f017764a80f57a1cefd2e0ca349c61bc2eb3948cb76998c97992aaa4497218b
7
+ data.tar.gz: aaeef8f54d352eb93649de28f2d54f107b1ac7e5d856505660f9a7a649aa8c436062216ffaf2ca5da04771e3cd35292b9963fa620fb91213bdcd804c904e8ca6
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.5.5
7
+ before_install: gem install bundler -v 2.0.1
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in git_miner.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ git_miner (0.1.0)
5
+ concurrent-ruby (~> 1.1)
6
+ parallel (~> 1.17)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ concurrent-ruby (1.1.5)
12
+ diff-lcs (1.3)
13
+ parallel (1.17.0)
14
+ rake (10.5.0)
15
+ rake-compiler (1.0.7)
16
+ rake
17
+ rspec (3.8.0)
18
+ rspec-core (~> 3.8.0)
19
+ rspec-expectations (~> 3.8.0)
20
+ rspec-mocks (~> 3.8.0)
21
+ rspec-core (3.8.0)
22
+ rspec-support (~> 3.8.0)
23
+ rspec-expectations (3.8.3)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.8.0)
26
+ rspec-mocks (3.8.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-support (3.8.0)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bundler (~> 2.0)
36
+ git_miner!
37
+ rake (~> 10.0)
38
+ rake-compiler (~> 1.0)
39
+ rspec (~> 3.0)
40
+
41
+ BUNDLED WITH
42
+ 2.0.1
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # GitMiner
2
+
3
+ Pet project I build experimenting with different concepts.
4
+
5
+ GitMiner allow for "mining" of vanity git prefixes.
6
+
7
+ This gem is a work in progress (core functionality is working).
8
+
9
+
10
+ ## Installation
11
+
12
+ Installation will add the `git-mine` binary.
13
+
14
+ ```ruby
15
+ gem 'git_miner'
16
+ ```
17
+
18
+
19
+ ## Usage
20
+
21
+ Though `git`, using `git mine [DESIRED_PREFIX]` will invoke the required logic.
22
+
23
+ Eg.:
24
+ ```
25
+ git mine c0ffee
26
+ ```
27
+
28
+ Some extra options are available. `git-mine -h` will display the help section (default options are optimised for performance).
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rake/extensiontask"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ Rake::ExtensionTask.new "git_miner_ext" do |ext|
10
+ ext.lib_dir = "lib/git_miner"
11
+ end
12
+
13
+ Rake::Task[:test].prerequisites << :compile
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "git_miner"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/git-mine ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ STDOUT.sync = true
4
+
5
+ require 'git_miner'
6
+
7
+
8
+ require 'optparse'
9
+
10
+ options = {
11
+ engine: :ruby,
12
+ dispatch: :parallel,
13
+ }
14
+
15
+ OptionParser.new do |opts|
16
+ opts.banner = "Usage: example.rb [options]"
17
+
18
+ opts.on("--engine [ruby|c]", [:ruby, :c], "Desired engine (default: ruby)") do |x|
19
+ options[:engine] = x
20
+ end
21
+
22
+ opts.on("--dispatch [simple|parallel]", [:simple, :parallel], "Desired dispatch (default: parallel)") do |x|
23
+ options[:dispatch] = x
24
+ end
25
+
26
+ opts.on_tail("-h", "--help", "Show this message") do
27
+ puts opts
28
+ exit
29
+ end
30
+ end.parse!
31
+
32
+ unless ARGV.size == 1
33
+ puts opts
34
+ exit 1
35
+ end
36
+
37
+ prefix = ARGV.first
38
+
39
+ engine = case(options[:engine])
40
+ when :ruby
41
+ GitMiner::Engine::RubyEngine
42
+ when :c
43
+ GitMiner::Engine::CExtensionEngine
44
+ else
45
+ raise ArgumentError, "unexpected engine"
46
+ end
47
+
48
+ dispatch = case(options[:dispatch])
49
+ when :simple
50
+ GitMiner::Dispatch::SimpleDispatch
51
+ when :parallel
52
+ GitMiner::Dispatch::ParallelDispatch
53
+ else
54
+ raise ArgumentError, "unexpected engine"
55
+ end
56
+
57
+ miner = GitMiner::Core.new(
58
+ engine: engine,
59
+ dispatch: dispatch,
60
+ prefix: prefix,
61
+ )
62
+ miner.validate
63
+ miner.mine
64
+ miner.report
65
+ miner.alter
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,116 @@
1
+ #include "ruby.h"
2
+ #include "openssl/sha.h"
3
+ #include <stdio.h>
4
+ #include <stdbool.h>
5
+
6
+ VALUE GitMinerExt = Qnil;
7
+
8
+ void Init_git_miner_ext();
9
+
10
+ VALUE rb_sha1_hexdigest(VALUE self, VALUE rbString);
11
+ VALUE rb_sha1_mine(VALUE self, int authorOffset, int committerOffset, int qty);
12
+
13
+ void Init_git_miner_ext() {
14
+ GitMinerExt = rb_define_module("GitMinerExt");
15
+ rb_define_method(GitMinerExt, "c_sha1_hexdigest", rb_sha1_hexdigest, 1);
16
+ rb_define_method(GitMinerExt, "c_sha1_mine", rb_sha1_mine, 3);
17
+ }
18
+
19
+ VALUE rb_sha1_hexdigest(VALUE self, VALUE rbString) {
20
+ unsigned char result[SHA_DIGEST_LENGTH];
21
+ const char *string = StringValueCStr(rbString);
22
+
23
+ SHA1(string, strlen(string), result);
24
+
25
+ unsigned char resultHex[SHA_DIGEST_LENGTH*2 + 1];
26
+ int i = 0;
27
+ for (i = 0; i < SHA_DIGEST_LENGTH; i++) {
28
+ sprintf((char*)&(resultHex[i*2]), "%02x", result[i]);
29
+ }
30
+
31
+ printf("ShaGenDebug: %s\n", resultHex);
32
+ return rb_str_new_cstr(resultHex);
33
+ }
34
+
35
+ VALUE rb_sha1_mine(VALUE self, int authorOffset, int committerOffset, int qty) {
36
+ VALUE rbPrefix = rb_iv_get(self, "@prefix");
37
+ char* prefix = StringValueCStr(rbPrefix);
38
+
39
+ VALUE rbTimestamp = rb_iv_get(self, "@timestamp");
40
+ long timestamp = FIX2LONG(rbTimestamp);
41
+
42
+ VALUE rbGitHeadTree = rb_iv_get(self, "@git_head_tree");
43
+ char* gitHeadTree = StringValueCStr(rbGitHeadTree);
44
+
45
+ VALUE rbGitHeadParent = rb_iv_get(self, "@git_head_parent");
46
+ char* gitHeadParent = StringValueCStr(rbGitHeadParent);
47
+
48
+ VALUE rbGitHeadAuthorPrefix = rb_iv_get(self, "@git_head_author_prefix");
49
+ char* gitHeadAuthorPrefix = StringValueCStr(rbGitHeadAuthorPrefix);
50
+
51
+ VALUE rbGitHeadCommitterPrefix = rb_iv_get(self, "@git_head_committer_prefix");
52
+ char* gitHeadCommitterPrefix = StringValueCStr(rbGitHeadCommitterPrefix);
53
+
54
+ VALUE rbGitHeadMessage = rb_iv_get(self, "@git_head_message");
55
+ char* gitHeadMessage = StringValueCStr(rbGitHeadMessage);
56
+
57
+ // TODO: init required or not?
58
+ int space = 10000;
59
+ char currentHead[space];
60
+ char commitPrefix[space];
61
+ char currentCommit[space];
62
+ for(int i = 0; i < space; i++) {
63
+ currentHead[i] = '\0';
64
+ commitPrefix[i] = '\0';
65
+ currentCommit[i] = '\0';
66
+ }
67
+
68
+ unsigned char currentSha[SHA_DIGEST_LENGTH];
69
+ unsigned char currentHex[SHA_DIGEST_LENGTH*2 + 1];
70
+
71
+ int shaLen;
72
+
73
+ for(int i = 0; i < qty; i++) {
74
+ sprintf(currentHead, "%s%s%s%d +0000\n%s%d +0000\n%s",
75
+ gitHeadTree,
76
+ gitHeadParent,
77
+ gitHeadAuthorPrefix,
78
+ timestamp - authorOffset,
79
+ gitHeadCommitterPrefix,
80
+ timestamp - committerOffset,
81
+ gitHeadMessage
82
+ );
83
+
84
+ sprintf(commitPrefix, "commit %d", strlen(currentHead));
85
+ sprintf(currentCommit, "%s_%s", commitPrefix, currentHead);
86
+ currentCommit[strlen(commitPrefix)] = '\0';
87
+
88
+ shaLen = strlen(commitPrefix) + 1 + strlen(currentHead);
89
+ SHA1(currentCommit, shaLen, currentSha);
90
+
91
+ for (int j = 0; j < SHA_DIGEST_LENGTH; j++) {
92
+ sprintf((char*)&(currentHex[j*2]), "%02x", currentSha[j]);
93
+ }
94
+
95
+ int prefixLen = strlen(prefix);
96
+ bool match = true;
97
+ for(int j = 0; j < prefixLen; j++) {
98
+ match = match && (prefix[j] == currentHex[j]);
99
+ }
100
+
101
+ if (match) {
102
+ rb_iv_set(self, "@result_current_sha", rb_str_new_cstr(currentHex));
103
+ rb_iv_set(self, "@result_author_offset", LONG2FIX(authorOffset));
104
+ rb_iv_set(self, "@result_committer_offset", LONG2FIX(committerOffset));
105
+ return Qnil;
106
+ }
107
+
108
+ committerOffset++;
109
+ if(committerOffset > authorOffset) {
110
+ authorOffset++;
111
+ committerOffset = 0;
112
+ }
113
+ }
114
+
115
+ return Qnil;
116
+ }
@@ -0,0 +1,11 @@
1
+ # Loads mkmf which is used to make makefiles for Ruby extensions
2
+ require 'mkmf'
3
+
4
+ # Give it a name
5
+ extension_name = 'git_miner_ext'
6
+
7
+ # The destination
8
+ dir_config(extension_name)
9
+
10
+ # Do the work
11
+ create_makefile(extension_name)
data/git_miner.gemspec ADDED
@@ -0,0 +1,45 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "git_miner/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "git_miner"
8
+ spec.version = GitMiner::VERSION
9
+ spec.authors = ["Thierry Joyal"]
10
+ spec.email = ["thierry.joyal@gmail.com"]
11
+
12
+ spec.summary = "git-mine shell executable"
13
+ spec.description = "git-mine provide mining logic for Git sha prefix"
14
+ spec.homepage = "https://github.com/tjoyal/git_miner"
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
20
+
21
+ spec.metadata["homepage_uri"] = spec.homepage
22
+ spec.metadata["source_code_uri"] = "https://github.com/tjoyal/git_miner"
23
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
24
+ else
25
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
26
+ end
27
+
28
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
29
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
30
+ end
31
+
32
+ spec.bindir = "bin"
33
+ spec.executables = ["git-mine"]
34
+ spec.require_paths = ["lib"]
35
+
36
+ spec.extensions = %w[ext/git_miner_ext/extconf.rb]
37
+
38
+ spec.add_runtime_dependency "parallel", "~> 1.17"
39
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.1"
40
+
41
+ spec.add_development_dependency "bundler", "~> 2.0"
42
+ spec.add_development_dependency "rake", "~> 10.0"
43
+ spec.add_development_dependency "rspec", "~> 3.0"
44
+ spec.add_development_dependency "rake-compiler", "~> 1.0"
45
+ end
data/lib/git_miner.rb ADDED
@@ -0,0 +1,8 @@
1
+ # Require all. Not ideal, only what is required based on the parameters should get to be required.
2
+ Dir[File.join(__dir__, 'git_miner', '**', '*.rb')].each do |file|
3
+ require file
4
+ end
5
+
6
+ module GitMiner
7
+ class Error < StandardError; end
8
+ end
@@ -0,0 +1,68 @@
1
+ module GitMiner
2
+ class Core
3
+ def initialize(engine:, dispatch:, prefix:)
4
+ puts "Initializing with engine '#{engine}'. dispatch '#{dispatch}' and prefix '#{prefix}'"
5
+
6
+ @prefix = prefix
7
+
8
+ @now = Time.now.utc
9
+
10
+ @engine = engine.new(
11
+ prefix: @prefix,
12
+ now: @now,
13
+ )
14
+
15
+ @dispatch = dispatch.new(
16
+ prefix: @prefix,
17
+ engine: @engine
18
+ )
19
+ end
20
+
21
+ def validate
22
+ str = "Test content"
23
+ expected = "bca20547e94049e1ffea27223581c567022a5774"
24
+
25
+ current = @engine.sha1(str)
26
+
27
+ unless expected == current
28
+ message = <<~ERROR
29
+ Invalid sha computation logic
30
+ Expected: #{expected} (#{expected.size})
31
+ Generated: #{current} (#{current.size})
32
+ ERROR
33
+
34
+ raise(message)
35
+ end
36
+
37
+ unless @prefix[/^[0-9a-f]{1,32}$/]
38
+ raise "Invalid prefix, expected '^[0-9a-f]{1,32}$'"
39
+ end
40
+
41
+ puts "Validations: Successful"
42
+ end
43
+
44
+ def mine
45
+ puts "Mining for sha"
46
+
47
+ @mining_result = @dispatch.execute
48
+ end
49
+
50
+ def report
51
+ raise "Prerequisite: Require mining to be completed first" unless @mining_result
52
+
53
+ puts "Mining results:"
54
+ puts "- New sha: #{@mining_result.sha}"
55
+ puts "- Author offset: #{@mining_result.author_offset}"
56
+ puts "- Committer offset: #{@mining_result.committer_offset}"
57
+ end
58
+
59
+ def alter
60
+ raise "Prerequisite: Require mining to be completed first" unless @mining_result
61
+
62
+ author_date = @now - @mining_result.author_offset
63
+ committer_date = @now - @mining_result.committer_offset
64
+
65
+ GitUtil.update_current_ref(author_date, committer_date)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,34 @@
1
+ module GitMiner
2
+ module Dispatch
3
+ class AbstractDispatch
4
+ BATCH_SIZE = 50_000
5
+
6
+ def initialize(prefix:, engine:)
7
+ @prefix = prefix
8
+ @engine = engine
9
+
10
+ @group_manager = GroupManager.new(batch_size: BATCH_SIZE)
11
+ end
12
+
13
+ def execute
14
+ thread = Thread.new do
15
+ progress = Progress.new(@prefix.length)
16
+ loop do
17
+ progress.tick(@group_manager.batch, @group_manager.count)
18
+ sleep 1
19
+ end
20
+ end
21
+
22
+ perform
23
+ ensure
24
+ thread.kill
25
+ end
26
+
27
+ private
28
+
29
+ def perform
30
+ raise NotImplementedError
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,48 @@
1
+ require 'parallel'
2
+ require 'concurrent'
3
+
4
+ module GitMiner
5
+ module Dispatch
6
+ class ParallelDispatch < AbstractDispatch
7
+ class ShaFound < StandardError
8
+ attr_reader :result
9
+
10
+ def initialize(result)
11
+ @result = result
12
+ end
13
+ end
14
+
15
+ def perform
16
+ Parallel.each(parallel_groups, in_processes: Concurrent.processor_count, preserve_results: false) do |group|
17
+ author_offset, committer_offset = group
18
+
19
+ result = @engine.mine(
20
+ author_offset: author_offset,
21
+ committer_offset: committer_offset,
22
+ qty: @group_manager.batch_size
23
+ )
24
+
25
+ if result
26
+ raise(ShaFound, result)
27
+ end
28
+ end
29
+ rescue ShaFound => e
30
+ e.result
31
+ end
32
+
33
+ def parallel_groups
34
+ -> do
35
+ author_offset = @group_manager.author_offset
36
+ committer_offset = @group_manager.committer_offset
37
+
38
+ @group_manager.advance!
39
+
40
+ [
41
+ author_offset,
42
+ committer_offset
43
+ ]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ module GitMiner
2
+ module Dispatch
3
+ class SimpleDispatch < AbstractDispatch
4
+ def perform
5
+ loop do
6
+ result = @engine.mine(
7
+ author_offset: @group_manager.author_offset,
8
+ committer_offset: @group_manager.committer_offset,
9
+ qty: @group_manager.batch_size
10
+ )
11
+
12
+ unless result
13
+ @group_manager.advance!
14
+ next
15
+ end
16
+
17
+ return result
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module GitMiner
2
+ module Engine
3
+ class AbstractEngine
4
+
5
+ def sha1(_str)
6
+ raise NotImplementedError
7
+ end
8
+
9
+ def initialize(prefix:, now:)
10
+ @prefix = prefix
11
+
12
+ @timestamp = now.to_i
13
+
14
+ git_head = GitUtil.commit_head
15
+ @git_head_tree ||= git_head[/^(tree.+?\n)/]
16
+ @git_head_parent ||= git_head[/^(parent.+?\n)/]
17
+ @git_head_author ||= git_head[/^(author.+?\n)/]
18
+ @git_head_committer ||= git_head[/^(committer.+?\n)/]
19
+ @git_head_message ||= git_head.gsub(/^(tree|parent|author|committer).+\n/, '')
20
+
21
+ @git_head_author_prefix = @git_head_author.sub(/\d{10}.+?\n/, '')
22
+ @git_head_committer_prefix = @git_head_committer.sub(/\d{10}.+?\n/, '')
23
+ end
24
+
25
+ def mine(author_offset:, committer_offset:, qty:)
26
+ raise NotImplementedError
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ require 'git_miner_ext'
2
+
3
+ module GitMiner
4
+ module Engine
5
+ class CExtensionEngine < AbstractEngine
6
+ include GitMinerExt
7
+
8
+ def sha1(str)
9
+ c_sha1_hexdigest(str)
10
+ end
11
+
12
+ def mine(author_offset:, committer_offset:, qty:)
13
+ c_sha1_mine(author_offset, committer_offset, qty)
14
+
15
+ # From C mutations
16
+ if @result_current_sha
17
+ MiningResult.new(
18
+ sha: @result_current_sha,
19
+ author_offset: @result_author_offset,
20
+ committer_offset: @result_committer_offset,
21
+ )
22
+ else
23
+ nil
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ require 'digest'
2
+
3
+ module GitMiner
4
+ module Engine
5
+ class RubyEngine < AbstractEngine
6
+ def sha1(str)
7
+ Digest::SHA1.hexdigest(str)
8
+ end
9
+
10
+ def mine(author_offset:, committer_offset:, qty:)
11
+ qty.times do
12
+ current_sha = generate_sha(author_offset, committer_offset)
13
+
14
+ if current_sha.start_with?(@prefix)
15
+ return MiningResult.new(
16
+ sha: current_sha,
17
+ author_offset: author_offset,
18
+ committer_offset: committer_offset,
19
+ )
20
+ end
21
+
22
+ committer_offset += 1
23
+ if committer_offset > author_offset
24
+ author_offset += 1
25
+ committer_offset = 0
26
+ end
27
+ end
28
+
29
+ return
30
+ end
31
+
32
+ private
33
+
34
+ # TODO: handle multiple author/committer
35
+ def generate_sha(author_offset, committer_offset)
36
+ commit_head = @git_head_tree
37
+ commit_head += @git_head_parent
38
+ commit_head += @git_head_author_prefix + (@timestamp - author_offset).to_s + " +0000\n"
39
+ commit_head += @git_head_committer_prefix + (@timestamp - committer_offset).to_s + " +0000\n"
40
+ commit_head += @git_head_message
41
+
42
+ str = "commit #{commit_head.size}\0#{commit_head}"
43
+ Digest::SHA1.hexdigest(str)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,53 @@
1
+ require 'open3'
2
+ require 'time'
3
+
4
+ module GitMiner
5
+ class GitUtil
6
+ class << self
7
+ def commit_head
8
+ # tree 2f18732a3837319fc7def49360366e435ca19700
9
+ # parent 94d9a9d715847d1fed730ef74a664a6773989a1b
10
+ # author Thierry Joyal <thierry.joyal@gmail.com> 1558198927 +0000
11
+ # committer Thierry Joyal <thierry.joyal@gmail.com> 1558198927 +0000
12
+ #
13
+ # This is the commit message
14
+ shell('git cat-file commit HEAD')
15
+ end
16
+
17
+ def update_current_ref(author_date, committer_date)
18
+ commit_object = shell("git cat-file -p HEAD | head -n 2").chomp
19
+ tree = shell("echo \"#{commit_object}\" | head -n 1 | cut -c 6-46").chomp
20
+ head = shell("echo \"#{commit_object}\" | tail -n 1 | cut -c 8-48").chomp
21
+ branch = shell("git branch | sed -n -e 's/^\\* \\(.*\\)/\\1/p'").chomp
22
+
23
+ environment = {
24
+ "GIT_AUTHOR_DATE" => author_date.iso8601,
25
+ "GIT_COMMITTER_DATE" => committer_date.iso8601,
26
+ }
27
+ cmd = "git commit-tree #{tree} -p #{head} -F -"
28
+ git_commit_message = shell('git log -1 --pretty=%B').chomp
29
+ new_sha = shell(cmd, environment: environment, stdin_data: git_commit_message).chomp
30
+
31
+ shell("git update-ref refs/heads/#{branch} #{new_sha}").chomp
32
+ end
33
+
34
+ private
35
+
36
+ def shell(cmd, environment: {}, stdin_data: nil)
37
+ puts "System call: #{cmd}"
38
+ puts "environment: #{environment.inspect}" unless environment.empty?
39
+ puts "stdin_data: #{stdin_data}" if stdin_data
40
+
41
+ output, status = Open3.capture2(environment, cmd, stdin_data: stdin_data) #, chdir: @working_directory
42
+
43
+ unless status.success?
44
+ raise "Error on system call: #{output}, #{status}"
45
+ end
46
+
47
+ puts "result: #{output}"
48
+
49
+ output
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,28 @@
1
+ module GitMiner
2
+ class GroupManager
3
+ attr_reader :author_offset, :batch_size, :committer_offset, :count, :batch
4
+
5
+ def initialize(batch_size:)
6
+ @batch_size = batch_size
7
+
8
+ @author_offset = 0
9
+ @committer_offset = 0
10
+ @batch = 0
11
+ @count = 0
12
+ end
13
+
14
+ def advance!
15
+ @batch_size.times do
16
+ @committer_offset += 1
17
+
18
+ if @committer_offset > @author_offset
19
+ @author_offset += 1
20
+ @committer_offset = 0
21
+ end
22
+ end
23
+
24
+ @count += @batch_size
25
+ @batch += 1
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ module GitMiner
2
+ class MiningResult
3
+ attr_reader :sha, :author_offset, :committer_offset
4
+
5
+ def initialize(sha:, author_offset:, committer_offset:)
6
+ @sha = sha
7
+ @author_offset = author_offset
8
+ @committer_offset = committer_offset
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,49 @@
1
+ module GitMiner
2
+ class Progress
3
+ HISTORIC_SIZE = 10
4
+
5
+ def initialize(prefix_length)
6
+ @combinations = 16 ** prefix_length
7
+
8
+ @historic = []
9
+ end
10
+
11
+ def tick(batch, count)
12
+ update_historic(batch, count)
13
+ report(count)
14
+ end
15
+
16
+ private
17
+
18
+ def update_historic(batch, count)
19
+ @historic << {
20
+ batch: batch,
21
+ count: count,
22
+ timestamp: Time.now
23
+ }
24
+
25
+ if @historic.size > HISTORIC_SIZE
26
+ @historic.shift
27
+ end
28
+ end
29
+
30
+ def report(count)
31
+ info = []
32
+
33
+ percentage = count * 100 / @combinations.to_f
34
+ info << "#{percentage.round(2)}%"
35
+
36
+ historic_count = @historic.last[:count] - @historic.first[:count]
37
+ historic_delay = @historic.last[:timestamp] - @historic.first[:timestamp]
38
+ if historic_delay > 0
39
+ per_sec = historic_count / historic_delay
40
+ info << "hash: #{per_sec.round(2)}/s"
41
+
42
+ per_sec = (@historic.last[:batch] - @historic.first[:batch]) / historic_delay
43
+ info << "batches: #{per_sec.round(2)}/s"
44
+ end
45
+
46
+ puts info.join(', ')
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module GitMiner
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: git_miner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Thierry Joyal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parallel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.17'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: concurrent-ruby
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake-compiler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.0'
97
+ description: git-mine provide mining logic for Git sha prefix
98
+ email:
99
+ - thierry.joyal@gmail.com
100
+ executables:
101
+ - git-mine
102
+ extensions:
103
+ - ext/git_miner_ext/extconf.rb
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".travis.yml"
109
+ - Gemfile
110
+ - Gemfile.lock
111
+ - README.md
112
+ - Rakefile
113
+ - bin/console
114
+ - bin/git-mine
115
+ - bin/setup
116
+ - ext/git_miner_ext/GitMinerExt.c
117
+ - ext/git_miner_ext/extconf.rb
118
+ - git_miner.gemspec
119
+ - lib/git_miner.rb
120
+ - lib/git_miner/core.rb
121
+ - lib/git_miner/dispatch/abstract_dispatch.rb
122
+ - lib/git_miner/dispatch/parallel_dispatch.rb
123
+ - lib/git_miner/dispatch/simple_dispatch.rb
124
+ - lib/git_miner/engine/abstract_engine.rb
125
+ - lib/git_miner/engine/c_extension_engine.rb
126
+ - lib/git_miner/engine/ruby_engine.rb
127
+ - lib/git_miner/git_util.rb
128
+ - lib/git_miner/group_manager.rb
129
+ - lib/git_miner/mining_result.rb
130
+ - lib/git_miner/progress.rb
131
+ - lib/git_miner/version.rb
132
+ homepage: https://github.com/tjoyal/git_miner
133
+ licenses: []
134
+ metadata:
135
+ homepage_uri: https://github.com/tjoyal/git_miner
136
+ source_code_uri: https://github.com/tjoyal/git_miner
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubygems_version: 3.0.3
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: git-mine shell executable
156
+ test_files: []