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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/bin/gitsha +59 -0
  3. data/ext/extconf.rb +9 -0
  4. data/ext/gitsha.c +245 -0
  5. metadata +49 -0
@@ -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
@@ -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}"
@@ -0,0 +1,9 @@
1
+ require "mkmf"
2
+
3
+ have_header "pthread.h"
4
+
5
+ pkg_config "openssl"
6
+ have_header "openssl/sha.h"
7
+ have_func "SHA1"
8
+
9
+ create_makefile "gitsha"
@@ -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: