change 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/LICENSE +18 -0
- data/README.md +54 -0
- data/Rakefile +13 -0
- data/bin/change +3 -0
- data/change.gemspec +25 -0
- data/ext/change/change.c +42 -0
- data/ext/change/extconf.rb +0 -0
- data/ext/change/makefile +8 -0
- data/ext/change/murmur3.c +315 -0
- data/ext/change/murmur3.h +21 -0
- data/lib/change.rb +114 -0
- data/spec/change_spec.rb +115 -0
- data/spec/fixture/modify.txt +0 -0
- data/spec/fixture/remove.txt +0 -0
- data/spec/spec_helper.rb +8 -0
- metadata +114 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2010
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
Change
|
2
|
+
======
|
3
|
+
|
4
|
+
What files changed since last time?
|
5
|
+
|
6
|
+
With dependency management and super fast hashing ([Murmur3](https://github.com/PeterScott/murmur3)).
|
7
|
+
|
8
|
+
[![Build Status](https://secure.travis-ci.org/winton/change.png)](http://travis-ci.org/winton/change)
|
9
|
+
|
10
|
+
Requirements
|
11
|
+
------------
|
12
|
+
|
13
|
+
gem install change
|
14
|
+
|
15
|
+
Changed?
|
16
|
+
--------
|
17
|
+
|
18
|
+
@change = Change.new("/absolute/path")
|
19
|
+
@change.d?("relative/path")
|
20
|
+
@change.d # { :add => [], :mod => [], :rem => [] }
|
21
|
+
@change.d(true) # reload
|
22
|
+
|
23
|
+
`Change` uses a YAML file stored in the root directory called `.change.yml` to maintain state.
|
24
|
+
|
25
|
+
Calling `@change.d?` does the following:
|
26
|
+
|
27
|
+
* Check if there is an entry for path in `.change.yml`
|
28
|
+
* If yes, read file size and compare with entry
|
29
|
+
* If file size matches, compare Murmur hash
|
30
|
+
* If no, record file size and Murmur hash
|
31
|
+
* Look up path in dependency tree
|
32
|
+
* If found, also mark all dependency parent paths as changed
|
33
|
+
|
34
|
+
Dependencies
|
35
|
+
------------
|
36
|
+
|
37
|
+
To group dependencies, `Change` uses the concept of a "session":
|
38
|
+
|
39
|
+
@change = Change.new("/absolute/path")
|
40
|
+
@change.s(:some_id) # start session with id
|
41
|
+
@change.r("relative/path") # record dependency
|
42
|
+
@change.s(nil) # stop session
|
43
|
+
|
44
|
+
If you use `@change.d?` within a session, it will return true if any dependencies have changed. `Change` recalls the dependencies from the last session to achieve this.
|
45
|
+
|
46
|
+
@change.s(:some_id)
|
47
|
+
@change.d?('relative/path')
|
48
|
+
# returns true if any dependencies have changed
|
49
|
+
# dependencies are recalled from LAST :some_id session
|
50
|
+
|
51
|
+
Recall files that were modified during the last execution of this session:
|
52
|
+
|
53
|
+
@change.s(:some_id)
|
54
|
+
@change.d_ # returns: { :add => [], :mod => [], :rem => [] }
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
|
4
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
5
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
6
|
+
end
|
7
|
+
|
8
|
+
task :default do
|
9
|
+
Dir.chdir("ext/change") do
|
10
|
+
system("make && make install")
|
11
|
+
end
|
12
|
+
Rake::Task['spec'].execute
|
13
|
+
end
|
data/bin/change
ADDED
data/change.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
root = File.expand_path('../', __FILE__)
|
3
|
+
lib = "#{root}/lib"
|
4
|
+
|
5
|
+
$:.unshift lib unless $:.include?(lib)
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "change"
|
9
|
+
s.version = '0.1.0'
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = [ 'Winton Welsh' ]
|
12
|
+
s.email = [ 'mail@wintoni.us' ]
|
13
|
+
s.homepage = "http://github.com/winton/change"
|
14
|
+
s.summary = %q{What files changed since last time?}
|
15
|
+
s.description = %q{What files changed since last time? With dependency management and super fast hashing (Murmur3).}
|
16
|
+
|
17
|
+
s.executables = `cd #{root} && git ls-files bin/*`.split("\n").collect { |f| File.basename(f) }
|
18
|
+
s.extensions = [ 'ext/change/extconf.rb' ]
|
19
|
+
s.files = `cd #{root} && git ls-files`.split("\n")
|
20
|
+
s.require_paths = %w(lib)
|
21
|
+
s.test_files = `cd #{root} && git ls-files -- {features,test,spec}/*`.split("\n")
|
22
|
+
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "rspec", "~> 1.0"
|
25
|
+
end
|
data/ext/change/change.c
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <stdlib.h>
|
3
|
+
#include <stdint.h>
|
4
|
+
#include <string.h>
|
5
|
+
#include "murmur3.h"
|
6
|
+
|
7
|
+
int main(int argc, char **argv) {
|
8
|
+
uint32_t hash[4]; /* Output for the hash */
|
9
|
+
uint32_t seed = 42; /* Seed value for hash */
|
10
|
+
|
11
|
+
int x;
|
12
|
+
|
13
|
+
for (x = 1; x < argc; x++) {
|
14
|
+
FILE *fh = fopen(argv[x], "rb");
|
15
|
+
|
16
|
+
if (fh != NULL) {
|
17
|
+
fseek(fh, 0L, SEEK_END);
|
18
|
+
long s = ftell(fh);
|
19
|
+
|
20
|
+
char *buffer = malloc(s + 1);
|
21
|
+
buffer[s] = '\0';
|
22
|
+
|
23
|
+
rewind(fh);
|
24
|
+
|
25
|
+
if (s != 0) {
|
26
|
+
fread(buffer, s, 1, fh);
|
27
|
+
fclose(fh);
|
28
|
+
fh = NULL;
|
29
|
+
}
|
30
|
+
|
31
|
+
MurmurHash3_x86_32(buffer, strlen(buffer), seed, hash);
|
32
|
+
printf("%08u\n", hash[0]);
|
33
|
+
|
34
|
+
free(buffer);
|
35
|
+
|
36
|
+
if (fh != NULL)
|
37
|
+
fclose(fh);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
return 0;
|
42
|
+
}
|
File without changes
|
data/ext/change/makefile
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
//-----------------------------------------------------------------------------
|
2
|
+
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
3
|
+
// domain. The author hereby disclaims copyright to this source code.
|
4
|
+
|
5
|
+
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
6
|
+
// algorithms are optimized for their respective platforms. You can still
|
7
|
+
// compile and run any of them on any platform, but your performance with the
|
8
|
+
// non-native version will be less than optimal.
|
9
|
+
|
10
|
+
#include "murmur3.h"
|
11
|
+
|
12
|
+
//-----------------------------------------------------------------------------
|
13
|
+
// Platform-specific functions and macros
|
14
|
+
|
15
|
+
#ifdef __GNUC__
|
16
|
+
#define FORCE_INLINE __attribute__((always_inline))
|
17
|
+
#else
|
18
|
+
#define FORCE_INLINE
|
19
|
+
#endif
|
20
|
+
|
21
|
+
static inline FORCE_INLINE uint32_t rotl32 ( uint32_t x, int8_t r )
|
22
|
+
{
|
23
|
+
return (x << r) | (x >> (32 - r));
|
24
|
+
}
|
25
|
+
|
26
|
+
static inline FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r )
|
27
|
+
{
|
28
|
+
return (x << r) | (x >> (64 - r));
|
29
|
+
}
|
30
|
+
|
31
|
+
#define ROTL32(x,y) rotl32(x,y)
|
32
|
+
#define ROTL64(x,y) rotl64(x,y)
|
33
|
+
|
34
|
+
#define BIG_CONSTANT(x) (x##LLU)
|
35
|
+
|
36
|
+
//-----------------------------------------------------------------------------
|
37
|
+
// Block read - if your platform needs to do endian-swapping or can only
|
38
|
+
// handle aligned reads, do the conversion here
|
39
|
+
|
40
|
+
#define getblock(p, i) (p[i])
|
41
|
+
|
42
|
+
//-----------------------------------------------------------------------------
|
43
|
+
// Finalization mix - force all bits of a hash block to avalanche
|
44
|
+
|
45
|
+
static inline FORCE_INLINE uint32_t fmix32 ( uint32_t h )
|
46
|
+
{
|
47
|
+
h ^= h >> 16;
|
48
|
+
h *= 0x85ebca6b;
|
49
|
+
h ^= h >> 13;
|
50
|
+
h *= 0xc2b2ae35;
|
51
|
+
h ^= h >> 16;
|
52
|
+
|
53
|
+
return h;
|
54
|
+
}
|
55
|
+
|
56
|
+
//----------
|
57
|
+
|
58
|
+
static inline FORCE_INLINE uint64_t fmix64 ( uint64_t k )
|
59
|
+
{
|
60
|
+
k ^= k >> 33;
|
61
|
+
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
|
62
|
+
k ^= k >> 33;
|
63
|
+
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
|
64
|
+
k ^= k >> 33;
|
65
|
+
|
66
|
+
return k;
|
67
|
+
}
|
68
|
+
|
69
|
+
//-----------------------------------------------------------------------------
|
70
|
+
|
71
|
+
void MurmurHash3_x86_32 ( const void * key, int len,
|
72
|
+
uint32_t seed, void * out )
|
73
|
+
{
|
74
|
+
const uint8_t * data = (const uint8_t*)key;
|
75
|
+
const int nblocks = len / 4;
|
76
|
+
int i;
|
77
|
+
|
78
|
+
uint32_t h1 = seed;
|
79
|
+
|
80
|
+
uint32_t c1 = 0xcc9e2d51;
|
81
|
+
uint32_t c2 = 0x1b873593;
|
82
|
+
|
83
|
+
//----------
|
84
|
+
// body
|
85
|
+
|
86
|
+
const uint32_t * blocks = (const uint32_t *)(data + nblocks*4);
|
87
|
+
|
88
|
+
for(i = -nblocks; i; i++)
|
89
|
+
{
|
90
|
+
uint32_t k1 = getblock(blocks,i);
|
91
|
+
|
92
|
+
k1 *= c1;
|
93
|
+
k1 = ROTL32(k1,15);
|
94
|
+
k1 *= c2;
|
95
|
+
|
96
|
+
h1 ^= k1;
|
97
|
+
h1 = ROTL32(h1,13);
|
98
|
+
h1 = h1*5+0xe6546b64;
|
99
|
+
}
|
100
|
+
|
101
|
+
//----------
|
102
|
+
// tail
|
103
|
+
|
104
|
+
const uint8_t * tail = (const uint8_t*)(data + nblocks*4);
|
105
|
+
|
106
|
+
uint32_t k1 = 0;
|
107
|
+
|
108
|
+
switch(len & 3)
|
109
|
+
{
|
110
|
+
case 3: k1 ^= tail[2] << 16;
|
111
|
+
case 2: k1 ^= tail[1] << 8;
|
112
|
+
case 1: k1 ^= tail[0];
|
113
|
+
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
114
|
+
};
|
115
|
+
|
116
|
+
//----------
|
117
|
+
// finalization
|
118
|
+
|
119
|
+
h1 ^= len;
|
120
|
+
|
121
|
+
h1 = fmix32(h1);
|
122
|
+
|
123
|
+
*(uint32_t*)out = h1;
|
124
|
+
}
|
125
|
+
|
126
|
+
//-----------------------------------------------------------------------------
|
127
|
+
|
128
|
+
void MurmurHash3_x86_128 ( const void * key, const int len,
|
129
|
+
uint32_t seed, void * out )
|
130
|
+
{
|
131
|
+
const uint8_t * data = (const uint8_t*)key;
|
132
|
+
const int nblocks = len / 16;
|
133
|
+
int i;
|
134
|
+
|
135
|
+
uint32_t h1 = seed;
|
136
|
+
uint32_t h2 = seed;
|
137
|
+
uint32_t h3 = seed;
|
138
|
+
uint32_t h4 = seed;
|
139
|
+
|
140
|
+
uint32_t c1 = 0x239b961b;
|
141
|
+
uint32_t c2 = 0xab0e9789;
|
142
|
+
uint32_t c3 = 0x38b34ae5;
|
143
|
+
uint32_t c4 = 0xa1e38b93;
|
144
|
+
|
145
|
+
//----------
|
146
|
+
// body
|
147
|
+
|
148
|
+
const uint32_t * blocks = (const uint32_t *)(data + nblocks*16);
|
149
|
+
|
150
|
+
for(i = -nblocks; i; i++)
|
151
|
+
{
|
152
|
+
uint32_t k1 = getblock(blocks,i*4+0);
|
153
|
+
uint32_t k2 = getblock(blocks,i*4+1);
|
154
|
+
uint32_t k3 = getblock(blocks,i*4+2);
|
155
|
+
uint32_t k4 = getblock(blocks,i*4+3);
|
156
|
+
|
157
|
+
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
158
|
+
|
159
|
+
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
|
160
|
+
|
161
|
+
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
162
|
+
|
163
|
+
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
|
164
|
+
|
165
|
+
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
166
|
+
|
167
|
+
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
|
168
|
+
|
169
|
+
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
170
|
+
|
171
|
+
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
|
172
|
+
}
|
173
|
+
|
174
|
+
//----------
|
175
|
+
// tail
|
176
|
+
|
177
|
+
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
178
|
+
|
179
|
+
uint32_t k1 = 0;
|
180
|
+
uint32_t k2 = 0;
|
181
|
+
uint32_t k3 = 0;
|
182
|
+
uint32_t k4 = 0;
|
183
|
+
|
184
|
+
switch(len & 15)
|
185
|
+
{
|
186
|
+
case 15: k4 ^= tail[14] << 16;
|
187
|
+
case 14: k4 ^= tail[13] << 8;
|
188
|
+
case 13: k4 ^= tail[12] << 0;
|
189
|
+
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
190
|
+
|
191
|
+
case 12: k3 ^= tail[11] << 24;
|
192
|
+
case 11: k3 ^= tail[10] << 16;
|
193
|
+
case 10: k3 ^= tail[ 9] << 8;
|
194
|
+
case 9: k3 ^= tail[ 8] << 0;
|
195
|
+
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
196
|
+
|
197
|
+
case 8: k2 ^= tail[ 7] << 24;
|
198
|
+
case 7: k2 ^= tail[ 6] << 16;
|
199
|
+
case 6: k2 ^= tail[ 5] << 8;
|
200
|
+
case 5: k2 ^= tail[ 4] << 0;
|
201
|
+
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
202
|
+
|
203
|
+
case 4: k1 ^= tail[ 3] << 24;
|
204
|
+
case 3: k1 ^= tail[ 2] << 16;
|
205
|
+
case 2: k1 ^= tail[ 1] << 8;
|
206
|
+
case 1: k1 ^= tail[ 0] << 0;
|
207
|
+
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
208
|
+
};
|
209
|
+
|
210
|
+
//----------
|
211
|
+
// finalization
|
212
|
+
|
213
|
+
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
214
|
+
|
215
|
+
h1 += h2; h1 += h3; h1 += h4;
|
216
|
+
h2 += h1; h3 += h1; h4 += h1;
|
217
|
+
|
218
|
+
h1 = fmix32(h1);
|
219
|
+
h2 = fmix32(h2);
|
220
|
+
h3 = fmix32(h3);
|
221
|
+
h4 = fmix32(h4);
|
222
|
+
|
223
|
+
h1 += h2; h1 += h3; h1 += h4;
|
224
|
+
h2 += h1; h3 += h1; h4 += h1;
|
225
|
+
|
226
|
+
((uint32_t*)out)[0] = h1;
|
227
|
+
((uint32_t*)out)[1] = h2;
|
228
|
+
((uint32_t*)out)[2] = h3;
|
229
|
+
((uint32_t*)out)[3] = h4;
|
230
|
+
}
|
231
|
+
|
232
|
+
//-----------------------------------------------------------------------------
|
233
|
+
|
234
|
+
void MurmurHash3_x64_128 ( const void * key, const int len,
|
235
|
+
const uint32_t seed, void * out )
|
236
|
+
{
|
237
|
+
const uint8_t * data = (const uint8_t*)key;
|
238
|
+
const int nblocks = len / 16;
|
239
|
+
int i;
|
240
|
+
|
241
|
+
uint64_t h1 = seed;
|
242
|
+
uint64_t h2 = seed;
|
243
|
+
|
244
|
+
uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5);
|
245
|
+
uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f);
|
246
|
+
|
247
|
+
//----------
|
248
|
+
// body
|
249
|
+
|
250
|
+
const uint64_t * blocks = (const uint64_t *)(data);
|
251
|
+
|
252
|
+
for(i = 0; i < nblocks; i++)
|
253
|
+
{
|
254
|
+
uint64_t k1 = getblock(blocks,i*2+0);
|
255
|
+
uint64_t k2 = getblock(blocks,i*2+1);
|
256
|
+
|
257
|
+
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
258
|
+
|
259
|
+
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
|
260
|
+
|
261
|
+
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
262
|
+
|
263
|
+
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
|
264
|
+
}
|
265
|
+
|
266
|
+
//----------
|
267
|
+
// tail
|
268
|
+
|
269
|
+
const uint8_t * tail = (const uint8_t*)(data + nblocks*16);
|
270
|
+
|
271
|
+
uint64_t k1 = 0;
|
272
|
+
uint64_t k2 = 0;
|
273
|
+
|
274
|
+
switch(len & 15)
|
275
|
+
{
|
276
|
+
case 15: k2 ^= (uint64_t)(tail[14]) << 48;
|
277
|
+
case 14: k2 ^= (uint64_t)(tail[13]) << 40;
|
278
|
+
case 13: k2 ^= (uint64_t)(tail[12]) << 32;
|
279
|
+
case 12: k2 ^= (uint64_t)(tail[11]) << 24;
|
280
|
+
case 11: k2 ^= (uint64_t)(tail[10]) << 16;
|
281
|
+
case 10: k2 ^= (uint64_t)(tail[ 9]) << 8;
|
282
|
+
case 9: k2 ^= (uint64_t)(tail[ 8]) << 0;
|
283
|
+
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
284
|
+
|
285
|
+
case 8: k1 ^= (uint64_t)(tail[ 7]) << 56;
|
286
|
+
case 7: k1 ^= (uint64_t)(tail[ 6]) << 48;
|
287
|
+
case 6: k1 ^= (uint64_t)(tail[ 5]) << 40;
|
288
|
+
case 5: k1 ^= (uint64_t)(tail[ 4]) << 32;
|
289
|
+
case 4: k1 ^= (uint64_t)(tail[ 3]) << 24;
|
290
|
+
case 3: k1 ^= (uint64_t)(tail[ 2]) << 16;
|
291
|
+
case 2: k1 ^= (uint64_t)(tail[ 1]) << 8;
|
292
|
+
case 1: k1 ^= (uint64_t)(tail[ 0]) << 0;
|
293
|
+
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
294
|
+
};
|
295
|
+
|
296
|
+
//----------
|
297
|
+
// finalization
|
298
|
+
|
299
|
+
h1 ^= len; h2 ^= len;
|
300
|
+
|
301
|
+
h1 += h2;
|
302
|
+
h2 += h1;
|
303
|
+
|
304
|
+
h1 = fmix64(h1);
|
305
|
+
h2 = fmix64(h2);
|
306
|
+
|
307
|
+
h1 += h2;
|
308
|
+
h2 += h1;
|
309
|
+
|
310
|
+
((uint64_t*)out)[0] = h1;
|
311
|
+
((uint64_t*)out)[1] = h2;
|
312
|
+
}
|
313
|
+
|
314
|
+
//-----------------------------------------------------------------------------
|
315
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
//-----------------------------------------------------------------------------
|
2
|
+
// MurmurHash3 was written by Austin Appleby, and is placed in the
|
3
|
+
// public domain. The author hereby disclaims copyright to this source
|
4
|
+
// code.
|
5
|
+
|
6
|
+
#ifndef _MURMURHASH3_H_
|
7
|
+
#define _MURMURHASH3_H_
|
8
|
+
|
9
|
+
#include <stdint.h>
|
10
|
+
|
11
|
+
//-----------------------------------------------------------------------------
|
12
|
+
|
13
|
+
void MurmurHash3_x86_32 (const void *key, int len, uint32_t seed, void *out);
|
14
|
+
|
15
|
+
void MurmurHash3_x86_128(const void *key, int len, uint32_t seed, void *out);
|
16
|
+
|
17
|
+
void MurmurHash3_x64_128(const void *key, int len, uint32_t seed, void *out);
|
18
|
+
|
19
|
+
//-----------------------------------------------------------------------------
|
20
|
+
|
21
|
+
#endif // _MURMURHASH3_H_
|
data/lib/change.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
$:.unshift File.dirname(__FILE__)
|
4
|
+
|
5
|
+
class Change
|
6
|
+
|
7
|
+
def initialize(root)
|
8
|
+
@root = File.expand_path(root)
|
9
|
+
end
|
10
|
+
|
11
|
+
def d?(path)
|
12
|
+
changed = @d.values.flatten
|
13
|
+
if @session && @last_deps && @last_deps.include?(path)
|
14
|
+
@last_deps.any? { |p| changed.include?(p) }
|
15
|
+
else
|
16
|
+
changed.include?(path)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def d(reload=false)
|
21
|
+
if @d && !reload
|
22
|
+
@d
|
23
|
+
else
|
24
|
+
paths = Dir.chdir(@root) { Dir["**/*"] }
|
25
|
+
add = paths - states.keys
|
26
|
+
rem = states.keys - paths
|
27
|
+
hashes = murmur_hashes(paths)
|
28
|
+
|
29
|
+
mod = paths.inject([]) do |array, path|
|
30
|
+
hash = hashes[path]
|
31
|
+
size = Dir.chdir(@root) { File.size(path) }
|
32
|
+
|
33
|
+
if states[path] && size != states[path][:size]
|
34
|
+
array << path
|
35
|
+
elsif states[path] && hash != states[path][:hash]
|
36
|
+
array << path
|
37
|
+
end
|
38
|
+
|
39
|
+
states[path] ||= {}
|
40
|
+
states[path][:hash] = hash
|
41
|
+
states[path][:size] = size
|
42
|
+
|
43
|
+
array
|
44
|
+
end
|
45
|
+
|
46
|
+
mod -= add
|
47
|
+
rem.each { |path| states.delete(path) }
|
48
|
+
|
49
|
+
write!
|
50
|
+
|
51
|
+
@d = { :add => add, :mod => mod, :rem => rem }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def d_
|
56
|
+
@last_session
|
57
|
+
end
|
58
|
+
|
59
|
+
def r(path)
|
60
|
+
if @session
|
61
|
+
deps[@session].push(path)
|
62
|
+
deps[@session].uniq!
|
63
|
+
write!
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def s(id)
|
68
|
+
d(true)
|
69
|
+
|
70
|
+
if @session && @session != id
|
71
|
+
@last_session = sessions[@session] = d
|
72
|
+
write!
|
73
|
+
end
|
74
|
+
|
75
|
+
@session = id
|
76
|
+
@last_deps = @session ? deps[@session] : nil
|
77
|
+
deps[@session] = [] if @session
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def data
|
83
|
+
@data ||= (YAML.load(File.read("#{@root}/.change.yml")) rescue {})
|
84
|
+
end
|
85
|
+
|
86
|
+
def murmur_bin
|
87
|
+
@murmur ||= File.expand_path('../../bin/murmur3', __FILE__)
|
88
|
+
end
|
89
|
+
|
90
|
+
def murmur_hashes(paths)
|
91
|
+
hashes = Dir.chdir(@root) do
|
92
|
+
`#{murmur_bin} #{paths.collect { |m| "'#{m}'" }.join(' ')}`
|
93
|
+
end
|
94
|
+
Hash[paths.zip(hashes.split("\n"))]
|
95
|
+
end
|
96
|
+
|
97
|
+
def deps
|
98
|
+
data['deps'] ||= {}
|
99
|
+
end
|
100
|
+
|
101
|
+
def sessions
|
102
|
+
data['sessions'] ||= {}
|
103
|
+
end
|
104
|
+
|
105
|
+
def states
|
106
|
+
data['states'] ||= {}
|
107
|
+
end
|
108
|
+
|
109
|
+
def write!
|
110
|
+
File.open("#{@root}/.change.yml", 'w') do |f|
|
111
|
+
f.write(YAML.dump(data))
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/spec/change_spec.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe ::Change do
|
5
|
+
|
6
|
+
before :all do
|
7
|
+
@root = File.expand_path('../../', __FILE__)
|
8
|
+
FileUtils.rm_rf(@tmp = "#{@root}/spec/tmp")
|
9
|
+
FileUtils.cp_r("#{@root}/spec/fixture", @tmp)
|
10
|
+
@change = ::Change.new(@tmp)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :d do
|
14
|
+
it "should return all files as added" do
|
15
|
+
@change.d.should == {
|
16
|
+
:add => [ "modify.txt", "remove.txt" ],
|
17
|
+
:mod => [],
|
18
|
+
:rem => []
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should cache last changes" do
|
23
|
+
@change.d.should == {
|
24
|
+
:add => [ "modify.txt", "remove.txt" ],
|
25
|
+
:mod => [],
|
26
|
+
:rem => []
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return no files modified" do
|
31
|
+
@change.d(true).should == {
|
32
|
+
:add => [],
|
33
|
+
:mod => [],
|
34
|
+
:rem => []
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return modified" do
|
39
|
+
File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!') }
|
40
|
+
@change.d(true).should == {
|
41
|
+
:add => [],
|
42
|
+
:mod => [ "modify.txt" ],
|
43
|
+
:rem => []
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should return removed" do
|
48
|
+
FileUtils.rm("#{@tmp}/remove.txt")
|
49
|
+
@change.d(true).should == {
|
50
|
+
:add => [],
|
51
|
+
:mod => [],
|
52
|
+
:rem => [ "remove.txt" ]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return added" do
|
57
|
+
File.open("#{@tmp}/add.txt", 'w') { |f| f.write('!') }
|
58
|
+
@change.d(true).should == {
|
59
|
+
:add => [ "add.txt" ],
|
60
|
+
:mod => [],
|
61
|
+
:rem => []
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should save state" do
|
66
|
+
@change = ::Change.new(@tmp)
|
67
|
+
@change.d.should == {
|
68
|
+
:add => [],
|
69
|
+
:mod => [],
|
70
|
+
:rem => []
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe :d? do
|
76
|
+
it "should return changed boolean" do
|
77
|
+
File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!!') }
|
78
|
+
@change.d(true)
|
79
|
+
@change.d?('add.txt').should == false
|
80
|
+
@change.d?('modify.txt').should == true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe :s do
|
85
|
+
before :all do
|
86
|
+
@change.s(:id)
|
87
|
+
@change.r('modify.txt')
|
88
|
+
@change.r('remove.txt')
|
89
|
+
File.open("#{@tmp}/add.txt", 'w') { |f| f.write('!') }
|
90
|
+
File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!!!') }
|
91
|
+
@change.s(nil)
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should record dependencies" do
|
95
|
+
@change.send(:deps).should == { :id => [ "modify.txt", "remove.txt" ] }
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should record files modified during session" do
|
99
|
+
@change.send(:sessions).should == {
|
100
|
+
:id => {
|
101
|
+
:add => [ "add.txt" ], :mod => [ "modify.txt" ], :rem => []
|
102
|
+
}
|
103
|
+
}
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should return proper data within next session" do
|
107
|
+
File.open("#{@tmp}/modify.txt", 'w') { |f| f.write('!!!!') }
|
108
|
+
@change.s(:id)
|
109
|
+
@change.d?("remove.txt").should == true
|
110
|
+
@change.d.should == { :add => [], :mod => [ "modify.txt" ], :rem => [] }
|
111
|
+
@change.d_.should == { :add => [ "add.txt" ], :mod => [ "modify.txt" ], :rem => [] }
|
112
|
+
@change.s(nil)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
File without changes
|
File without changes
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: change
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Winton Welsh
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-07-07 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rake
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rspec
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 15
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 0
|
46
|
+
version: "1.0"
|
47
|
+
type: :development
|
48
|
+
version_requirements: *id002
|
49
|
+
description: What files changed since last time? With dependency management and super fast hashing (Murmur3).
|
50
|
+
email:
|
51
|
+
- mail@wintoni.us
|
52
|
+
executables:
|
53
|
+
- change
|
54
|
+
extensions:
|
55
|
+
- ext/change/extconf.rb
|
56
|
+
extra_rdoc_files: []
|
57
|
+
|
58
|
+
files:
|
59
|
+
- .gitignore
|
60
|
+
- .travis.yml
|
61
|
+
- Gemfile
|
62
|
+
- LICENSE
|
63
|
+
- README.md
|
64
|
+
- Rakefile
|
65
|
+
- bin/change
|
66
|
+
- change.gemspec
|
67
|
+
- ext/change/change.c
|
68
|
+
- ext/change/extconf.rb
|
69
|
+
- ext/change/makefile
|
70
|
+
- ext/change/murmur3.c
|
71
|
+
- ext/change/murmur3.h
|
72
|
+
- lib/change.rb
|
73
|
+
- spec/change_spec.rb
|
74
|
+
- spec/fixture/modify.txt
|
75
|
+
- spec/fixture/remove.txt
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
homepage: http://github.com/winton/change
|
78
|
+
licenses: []
|
79
|
+
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
requirements: []
|
104
|
+
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.8.24
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: What files changed since last time?
|
110
|
+
test_files:
|
111
|
+
- spec/change_spec.rb
|
112
|
+
- spec/fixture/modify.txt
|
113
|
+
- spec/fixture/remove.txt
|
114
|
+
- spec/spec_helper.rb
|