gitsha 0.1.0
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 +7 -0
- data/bin/gitsha +59 -0
- data/ext/extconf.rb +9 -0
- data/ext/gitsha.c +245 -0
- metadata +49 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8371cd94164736da42ca2772f63675ee952f7c3f
|
4
|
+
data.tar.gz: 6bcfc8e581902d1ebebac611f0eb26b09f6f511b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4edb3450e5d6e25408c8ce16611f838adbfb3dded70b03ade7cc56119f846b1588ec723589dc97a0cd3da8501402cb26c23f971b86ad45cf8c46308ab209a1ff
|
7
|
+
data.tar.gz: b7ea22601ec9ee0cdb875f98edcd57ead242f434ecad5d5e8436fcc0533fd7966e0b9d8acfaa8d8d7c80a637b95b8ea29d5c92c708abc928561e93a5e48de0c7
|
data/bin/gitsha
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "digest/sha1"
|
3
|
+
require "zlib"
|
4
|
+
require "gitsha"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
if ARGV.size != 2
|
8
|
+
$stderr.puts "Usage: #$0 <ref> <new sha prefix>"
|
9
|
+
exit! false
|
10
|
+
end
|
11
|
+
|
12
|
+
ref, sha_prefix = ARGV
|
13
|
+
|
14
|
+
unless sha_prefix =~ /\A[a-f0-9]{,40}\z/
|
15
|
+
$stderr.puts "fatal: sha prefix in invalid format"
|
16
|
+
exit! false
|
17
|
+
end
|
18
|
+
|
19
|
+
git_repo = `git rev-parse --show-toplevel`.chomp
|
20
|
+
exit! false unless $?.success?
|
21
|
+
Dir.chdir git_repo
|
22
|
+
|
23
|
+
old_sha = IO.popen(["git", "rev-parse", ref], err: [:child, :out]).gets.chomp
|
24
|
+
unless old_sha =~ /\A[a-f0-9]{40}\z/
|
25
|
+
$stderr.puts old_sha
|
26
|
+
exit! false
|
27
|
+
end
|
28
|
+
|
29
|
+
commit_header, commit_data = Zlib::Inflate.inflate(File.read(".git/objects/#{old_sha[0...2]}/#{old_sha[2..-1]}")).split("\0", 2)
|
30
|
+
unless commit_header =~ /\Acommit /
|
31
|
+
$stderr.puts "fatal: #{old_sha} is not a commit"
|
32
|
+
exit! false
|
33
|
+
end
|
34
|
+
|
35
|
+
sha_prefix_half_hex_dig = sha_prefix.length.odd?
|
36
|
+
|
37
|
+
sha_prefix = sha_prefix.chars.each_slice(2).map { |a,b|
|
38
|
+
"#{a}#{b || 0}".to_i(16).chr
|
39
|
+
}.join.force_encoding("ASCII-8BIT")
|
40
|
+
|
41
|
+
cpus = case `uname`.chomp
|
42
|
+
when "Darwin"
|
43
|
+
`sysctl -n hw.ncpu`.to_i
|
44
|
+
when "Linux"
|
45
|
+
`nproc`.to_i
|
46
|
+
else
|
47
|
+
1
|
48
|
+
end
|
49
|
+
|
50
|
+
new_commit_data, new_sha = GitSha.bruteforce!(commit_data, sha_prefix, sha_prefix_half_hex_dig, cpus)
|
51
|
+
|
52
|
+
new_sha_hex = new_sha.bytes.map { |b| "%02x" % b.ord }.join
|
53
|
+
|
54
|
+
FileUtils.mkdir_p(".git/objects/#{new_sha_hex[0...2]}")
|
55
|
+
File.open(".git/objects/#{new_sha_hex[0...2]}/#{new_sha_hex[2..-1]}", "wb") do |f|
|
56
|
+
f.write Zlib::Deflate.deflate(new_commit_data)
|
57
|
+
end
|
58
|
+
|
59
|
+
puts "wrote commit: #{new_sha_hex}"
|
data/ext/extconf.rb
ADDED
data/ext/gitsha.c
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <signal.h>
|
3
|
+
#include <pthread.h>
|
4
|
+
#include <openssl/sha.h>
|
5
|
+
|
6
|
+
static const char*
|
7
|
+
hex_lut = "0123456789abcdef";
|
8
|
+
|
9
|
+
static size_t
|
10
|
+
header_len(size_t data_len)
|
11
|
+
{
|
12
|
+
char buff[64];
|
13
|
+
sprintf(buff, "commit %zu", data_len);
|
14
|
+
return strlen(buff) + 1;
|
15
|
+
}
|
16
|
+
|
17
|
+
static void
|
18
|
+
write_counter_hex(char* buff, uint64_t counter)
|
19
|
+
{
|
20
|
+
int i;
|
21
|
+
for(i = 0; i < 16; i++) {
|
22
|
+
buff[i] = hex_lut[(counter >> (i * 4)) & 0xf];
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
typedef struct {
|
27
|
+
pthread_t thread;
|
28
|
+
pthread_mutex_t* finished_mutex;
|
29
|
+
pthread_cond_t* finished_cond_var;
|
30
|
+
/* worker args: */
|
31
|
+
char* scratch_buff;
|
32
|
+
size_t scratch_len;
|
33
|
+
size_t counter_offset;
|
34
|
+
char* prefix;
|
35
|
+
size_t prefix_len;
|
36
|
+
unsigned char prefix_half_dig;
|
37
|
+
char prefix_has_half_dig;
|
38
|
+
size_t counter;
|
39
|
+
int stride;
|
40
|
+
/* worker output: */
|
41
|
+
int complete;
|
42
|
+
unsigned char sha[20];
|
43
|
+
}
|
44
|
+
worker_t;
|
45
|
+
|
46
|
+
static void
|
47
|
+
bruteforce_loop(char* output_sha, char* scratch_buff, size_t scratch_len, size_t counter_offset, char* prefix, size_t prefix_len, size_t counter, int stride)
|
48
|
+
{
|
49
|
+
unsigned char sha[20];
|
50
|
+
|
51
|
+
while(1) {
|
52
|
+
write_counter_hex(scratch_buff + counter_offset, counter);
|
53
|
+
SHA1((unsigned char*)scratch_buff, scratch_len, sha);
|
54
|
+
if(memcmp(sha, prefix, prefix_len) == 0) {
|
55
|
+
memcpy(output_sha, sha, 20);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
counter += stride;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
static void
|
63
|
+
bruteforce_loop_with_half_dig(char* output_sha, char* scratch_buff, size_t scratch_len, size_t counter_offset, char* prefix, size_t prefix_len, size_t counter, int stride, unsigned char half_dig)
|
64
|
+
{
|
65
|
+
unsigned char sha[20];
|
66
|
+
|
67
|
+
while(1) {
|
68
|
+
write_counter_hex(scratch_buff + counter_offset, counter);
|
69
|
+
SHA1((unsigned char*)scratch_buff, scratch_len, sha);
|
70
|
+
if((prefix_len == 0 || memcmp(sha, prefix, prefix_len) == 0) && (sha[prefix_len] >> 4) == half_dig) {
|
71
|
+
memcpy(output_sha, sha, 20);
|
72
|
+
return;
|
73
|
+
}
|
74
|
+
counter += stride;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
static void
|
79
|
+
thread_term()
|
80
|
+
{
|
81
|
+
pthread_exit(NULL);
|
82
|
+
}
|
83
|
+
|
84
|
+
static void*
|
85
|
+
worker_thread_main(void* arg)
|
86
|
+
{
|
87
|
+
worker_t* w = arg;
|
88
|
+
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
|
89
|
+
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
90
|
+
signal(SIGTERM, thread_term);
|
91
|
+
if(w->prefix_has_half_dig) {
|
92
|
+
bruteforce_loop_with_half_dig(
|
93
|
+
(char*)w->sha,
|
94
|
+
w->scratch_buff,
|
95
|
+
w->scratch_len,
|
96
|
+
w->counter_offset,
|
97
|
+
w->prefix,
|
98
|
+
w->prefix_len,
|
99
|
+
w->counter,
|
100
|
+
w->stride,
|
101
|
+
w->prefix_half_dig);
|
102
|
+
} else {
|
103
|
+
bruteforce_loop(
|
104
|
+
(char*)w->sha,
|
105
|
+
w->scratch_buff,
|
106
|
+
w->scratch_len,
|
107
|
+
w->counter_offset,
|
108
|
+
w->prefix,
|
109
|
+
w->prefix_len,
|
110
|
+
w->counter,
|
111
|
+
w->stride);
|
112
|
+
}
|
113
|
+
w->complete = 1;
|
114
|
+
pthread_mutex_lock(w->finished_mutex);
|
115
|
+
pthread_cond_signal(w->finished_cond_var);
|
116
|
+
pthread_mutex_unlock(w->finished_mutex);
|
117
|
+
return NULL;
|
118
|
+
}
|
119
|
+
|
120
|
+
static void
|
121
|
+
setup_worker(worker_t* w, char* commit_data, size_t commit_data_len, char* prefix, size_t prefix_len, char prefix_has_half_dig, int i, int ncpus)
|
122
|
+
{
|
123
|
+
size_t data_length = commit_data_len + 2 + 16;
|
124
|
+
size_t header_length = header_len(data_length);
|
125
|
+
size_t total_length = header_length + data_length;
|
126
|
+
|
127
|
+
char* prefix_copy = malloc(prefix_len);
|
128
|
+
char* scratch_buff = malloc(total_length + 1);
|
129
|
+
|
130
|
+
memcpy(prefix_copy, prefix, prefix_len);
|
131
|
+
sprintf(scratch_buff, "commit %zu", data_length);
|
132
|
+
|
133
|
+
memcpy(scratch_buff + header_length, commit_data, commit_data_len);
|
134
|
+
scratch_buff[header_length + commit_data_len] = '\n';
|
135
|
+
scratch_buff[header_length + commit_data_len + 1] = '\n';
|
136
|
+
|
137
|
+
w->scratch_buff = scratch_buff;
|
138
|
+
w->scratch_len = total_length;
|
139
|
+
w->counter_offset = header_length + commit_data_len + 2;
|
140
|
+
w->prefix = prefix_copy;
|
141
|
+
if(prefix_has_half_dig) {
|
142
|
+
w->prefix_len = prefix_len - 1;
|
143
|
+
w->prefix_half_dig = (unsigned char)prefix[prefix_len - 1] >> 4;
|
144
|
+
w->prefix_has_half_dig = 1;
|
145
|
+
} else {
|
146
|
+
w->prefix_len = prefix_len;
|
147
|
+
w->prefix_has_half_dig = 0;
|
148
|
+
}
|
149
|
+
w->counter = i;
|
150
|
+
w->stride = ncpus;
|
151
|
+
}
|
152
|
+
|
153
|
+
static void
|
154
|
+
destroy_worker(worker_t* w)
|
155
|
+
{
|
156
|
+
pthread_kill(w->thread, SIGTERM);
|
157
|
+
pthread_join(w->thread, NULL);
|
158
|
+
free(w->scratch_buff);
|
159
|
+
free(w->prefix);
|
160
|
+
}
|
161
|
+
|
162
|
+
static VALUE
|
163
|
+
start_bruteforce(char* commit_data, size_t commit_data_len, char* prefix, size_t prefix_len, int has_half_dig, int ncpus)
|
164
|
+
{
|
165
|
+
worker_t* workers = malloc(sizeof(worker_t) * ncpus);
|
166
|
+
int i;
|
167
|
+
VALUE ret = Qnil;
|
168
|
+
|
169
|
+
pthread_mutex_t finished_mutex;
|
170
|
+
pthread_cond_t finished_cond_var;
|
171
|
+
|
172
|
+
pthread_mutex_init(&finished_mutex, NULL);
|
173
|
+
pthread_cond_init(&finished_cond_var, NULL);
|
174
|
+
|
175
|
+
for(i = 0; i < ncpus; i++) {
|
176
|
+
setup_worker(&workers[i], commit_data, commit_data_len, prefix, prefix_len, has_half_dig, i, ncpus);
|
177
|
+
workers[i].complete = 0;
|
178
|
+
workers[i].finished_mutex = &finished_mutex;
|
179
|
+
workers[i].finished_cond_var = &finished_cond_var;
|
180
|
+
}
|
181
|
+
|
182
|
+
for(i = 0; i < ncpus; i++) {
|
183
|
+
pthread_create(&workers[i].thread, NULL, worker_thread_main, &workers[i]);
|
184
|
+
}
|
185
|
+
|
186
|
+
pthread_cond_wait(&finished_cond_var, &finished_mutex);
|
187
|
+
|
188
|
+
for(i = 0; i < ncpus; i++) {
|
189
|
+
if(workers[i].complete) {
|
190
|
+
ret = rb_ary_new3(2,
|
191
|
+
rb_str_new(workers[i].scratch_buff, workers[i].scratch_len),
|
192
|
+
rb_str_new((char*)workers[i].sha, 20));
|
193
|
+
break;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
for(i = 0; i < ncpus; i++) {
|
198
|
+
destroy_worker(&workers[i]);
|
199
|
+
}
|
200
|
+
|
201
|
+
pthread_mutex_unlock(&finished_mutex);
|
202
|
+
pthread_cond_destroy(&finished_cond_var);
|
203
|
+
pthread_mutex_destroy(&finished_mutex);
|
204
|
+
|
205
|
+
free(workers);
|
206
|
+
|
207
|
+
return ret;
|
208
|
+
}
|
209
|
+
|
210
|
+
static VALUE
|
211
|
+
bruteforce(VALUE _, VALUE commit_data, VALUE sha_prefix, VALUE sha_prefix_half_hex_dig, VALUE ncpus)
|
212
|
+
{
|
213
|
+
if(TYPE(commit_data) != T_STRING || TYPE(sha_prefix) != T_STRING) {
|
214
|
+
rb_raise(rb_eTypeError, "expected commit_data, sha_prefix to be strings");
|
215
|
+
}
|
216
|
+
|
217
|
+
if(TYPE(ncpus) != T_FIXNUM) {
|
218
|
+
rb_raise(rb_eTypeError, "expected ncpus to be a fixnum");
|
219
|
+
}
|
220
|
+
|
221
|
+
if(FIX2INT(ncpus) <= 0) {
|
222
|
+
rb_raise(rb_eTypeError, "expected ncpus to be > 0");
|
223
|
+
}
|
224
|
+
|
225
|
+
if(RSTRING_LEN(sha_prefix) > 20) {
|
226
|
+
rb_raise(rb_eArgError, "expected sha_prefix to be at most 20 bytes long");
|
227
|
+
}
|
228
|
+
|
229
|
+
return start_bruteforce(
|
230
|
+
RSTRING_PTR(commit_data),
|
231
|
+
RSTRING_LEN(commit_data),
|
232
|
+
RSTRING_PTR(sha_prefix),
|
233
|
+
RSTRING_LEN(sha_prefix),
|
234
|
+
RTEST(sha_prefix_half_hex_dig),
|
235
|
+
FIX2INT(ncpus));
|
236
|
+
}
|
237
|
+
|
238
|
+
void
|
239
|
+
Init_gitsha()
|
240
|
+
{
|
241
|
+
VALUE GitSha;
|
242
|
+
|
243
|
+
GitSha = rb_define_module("GitSha");
|
244
|
+
rb_define_singleton_method(GitSha, "bruteforce!", bruteforce, 4);
|
245
|
+
}
|
metadata
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gitsha
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Charlie Somerville
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-05-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: bruteforces git commit shas.
|
14
|
+
email: charlie@charliesomerville.com
|
15
|
+
executables:
|
16
|
+
- gitsha
|
17
|
+
extensions:
|
18
|
+
- ext/extconf.rb
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/gitsha
|
22
|
+
- ext/extconf.rb
|
23
|
+
- ext/gitsha.c
|
24
|
+
homepage: https://github.com/charliesome/gitsha
|
25
|
+
licenses:
|
26
|
+
- Simplified BSD
|
27
|
+
metadata: {}
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
requirements: []
|
43
|
+
rubyforge_project:
|
44
|
+
rubygems_version: 2.0.0
|
45
|
+
signing_key:
|
46
|
+
specification_version: 4
|
47
|
+
summary: bruteforces git commit shas
|
48
|
+
test_files: []
|
49
|
+
has_rdoc:
|