git-object-browser 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +13 -0
- data/Guardfile +11 -0
- data/LICENSE +22 -0
- data/README.md +18 -0
- data/Rakefile +2 -0
- data/bin/git-object-browser +5 -0
- data/git-object-browser.gemspec +18 -0
- data/htdocs/css/angular-ui.min.css +1 -0
- data/htdocs/css/bootstrap-responsive.css +1058 -0
- data/htdocs/css/bootstrap-responsive.min.css +9 -0
- data/htdocs/css/bootstrap.css +5389 -0
- data/htdocs/css/bootstrap.min.css +699 -0
- data/htdocs/css/font-awesome-ie7.css +645 -0
- data/htdocs/css/font-awesome.css +303 -0
- data/htdocs/css/main.css +22 -0
- data/htdocs/font/fontawesome-webfont.eot +0 -0
- data/htdocs/font/fontawesome-webfont.svg +255 -0
- data/htdocs/font/fontawesome-webfont.ttf +0 -0
- data/htdocs/font/fontawesome-webfont.woff +0 -0
- data/htdocs/img/glyphicons-halflings-white.png +0 -0
- data/htdocs/img/glyphicons-halflings.png +0 -0
- data/htdocs/index.html +114 -0
- data/htdocs/js/main.js +286 -0
- data/htdocs/js/vendor/angular-bootstrap-prettify.min.js +41 -0
- data/htdocs/js/vendor/angular-bootstrap.min.js +9 -0
- data/htdocs/js/vendor/angular-cookies.min.js +7 -0
- data/htdocs/js/vendor/angular-loader.min.js +7 -0
- data/htdocs/js/vendor/angular-resource.min.js +10 -0
- data/htdocs/js/vendor/angular-sanitize.min.js +13 -0
- data/htdocs/js/vendor/angular-ui-ieshiv.min.js +7 -0
- data/htdocs/js/vendor/angular-ui.min.js +7 -0
- data/htdocs/js/vendor/angular.js +14401 -0
- data/htdocs/js/vendor/angular.min.js +158 -0
- data/htdocs/js/vendor/bootstrap.js +2027 -0
- data/htdocs/js/vendor/bootstrap.min.js +6 -0
- data/htdocs/js/vendor/html5shiv.js +5 -0
- data/htdocs/js/vendor/jquery-1.8.2.min.js +2 -0
- data/htdocs/templates/directory.html +25 -0
- data/htdocs/templates/file.html +6 -0
- data/htdocs/templates/git.html +1 -0
- data/htdocs/templates/index.html +44 -0
- data/htdocs/templates/index_entry.html +54 -0
- data/htdocs/templates/info_refs.html +27 -0
- data/htdocs/templates/loading.html +1 -0
- data/htdocs/templates/notfound.html +7 -0
- data/htdocs/templates/object.html +54 -0
- data/htdocs/templates/objects.html +16 -0
- data/htdocs/templates/pack_file.html +15 -0
- data/htdocs/templates/pack_index.html +38 -0
- data/htdocs/templates/packed_object.html +111 -0
- data/htdocs/templates/packed_refs.html +27 -0
- data/htdocs/templates/ref.html +12 -0
- data/lib/git-object-browser.rb +37 -0
- data/lib/git-object-browser/dumper.rb +62 -0
- data/lib/git-object-browser/index_dumper.rb +77 -0
- data/lib/git-object-browser/main.rb +63 -0
- data/lib/git-object-browser/models/bindata.rb +68 -0
- data/lib/git-object-browser/models/directory.rb +61 -0
- data/lib/git-object-browser/models/git_object.rb +127 -0
- data/lib/git-object-browser/models/index.rb +77 -0
- data/lib/git-object-browser/models/index_entry.rb +95 -0
- data/lib/git-object-browser/models/index_reuc_extension.rb +31 -0
- data/lib/git-object-browser/models/index_tree_extension.rb +52 -0
- data/lib/git-object-browser/models/info_refs.rb +32 -0
- data/lib/git-object-browser/models/pack_file.rb +40 -0
- data/lib/git-object-browser/models/pack_index.rb +146 -0
- data/lib/git-object-browser/models/packed_object.rb +239 -0
- data/lib/git-object-browser/models/packed_refs.rb +39 -0
- data/lib/git-object-browser/models/plain_file.rb +24 -0
- data/lib/git-object-browser/models/ref.rb +32 -0
- data/lib/git-object-browser/object_dumper.rb +31 -0
- data/lib/git-object-browser/server/git_servlet.rb +209 -0
- data/lib/git-object-browser/server/main.rb +31 -0
- data/lib/git-object-browser/version.rb +3 -0
- data/spec/fixtures/generate_worktree.sh +103 -0
- data/spec/fixtures/git/indexes/001 +0 -0
- data/spec/fixtures/git/indexes/002-empty-tree-extension +0 -0
- data/spec/fixtures/git/plain_file +1 -0
- data/spec/fixtures/json/blob.json +9 -0
- data/spec/fixtures/json/merge-a.json +48 -0
- data/spec/fixtures/json/test3-tag.json +36 -0
- data/spec/fixtures/json/tree.json +20 -0
- data/spec/fixtures/worktree/_git/COMMIT_EDITMSG +1 -0
- data/spec/fixtures/worktree/_git/HEAD +1 -0
- data/spec/fixtures/worktree/_git/ORIG_HEAD +1 -0
- data/spec/fixtures/worktree/_git/config +11 -0
- data/spec/fixtures/worktree/_git/description +1 -0
- data/spec/fixtures/worktree/_git/hooks/applypatch-msg.sample +15 -0
- data/spec/fixtures/worktree/_git/hooks/commit-msg.sample +24 -0
- data/spec/fixtures/worktree/_git/hooks/post-update.sample +8 -0
- data/spec/fixtures/worktree/_git/hooks/pre-applypatch.sample +14 -0
- data/spec/fixtures/worktree/_git/hooks/pre-commit.sample +50 -0
- data/spec/fixtures/worktree/_git/hooks/pre-rebase.sample +169 -0
- data/spec/fixtures/worktree/_git/hooks/prepare-commit-msg.sample +36 -0
- data/spec/fixtures/worktree/_git/hooks/update.sample +128 -0
- data/spec/fixtures/worktree/_git/ignore +3 -0
- data/spec/fixtures/worktree/_git/index +0 -0
- data/spec/fixtures/worktree/_git/info/exclude +6 -0
- data/spec/fixtures/worktree/_git/info/refs +1 -0
- data/spec/fixtures/worktree/_git/logs/HEAD +10 -0
- data/spec/fixtures/worktree/_git/logs/refs/heads/branch-a +3 -0
- data/spec/fixtures/worktree/_git/logs/refs/heads/branch-b +2 -0
- data/spec/fixtures/worktree/_git/logs/refs/heads/master +4 -0
- data/spec/fixtures/worktree/_git/objects/00/cb8bfeb5b8ce906d39698e4e33b38341f5448f +1 -0
- data/spec/fixtures/worktree/_git/objects/07/31f9d4b6fa0475872be6a8ca263096f1d201cf +2 -0
- data/spec/fixtures/worktree/_git/objects/1d/3dc60b5a117054e43741d51e599ff31bb15f9f +0 -0
- data/spec/fixtures/worktree/_git/objects/26/4e42b1fef5bcb55acec162fdd5a068d79ae551 +0 -0
- data/spec/fixtures/worktree/_git/objects/28/3c06ddf1b31c14bb221d41173299e133b7753d +0 -0
- data/spec/fixtures/worktree/_git/objects/37/d1632d3f1159dad9cfb58e6c34312ab4355c49 +0 -0
- data/spec/fixtures/worktree/_git/objects/3a/2bf444f105c19b13ba5e75e884e10715e95a91 +0 -0
- data/spec/fixtures/worktree/_git/objects/40/a061aaf0cd0555449671a11993e4fed11f91a4 +2 -0
- data/spec/fixtures/worktree/_git/objects/53/2a1874c26cd19bd4d66e03218ab73e63de4357 +3 -0
- data/spec/fixtures/worktree/_git/objects/5b/719b165fde1964fb5a08adaf3b6e4f57ca1ff5 +0 -0
- data/spec/fixtures/worktree/_git/objects/61/cae34206bb889bae43ffdd22c17217485178bf +0 -0
- data/spec/fixtures/worktree/_git/objects/6c/444ac15f1e3c2a6869bd36ca7e58c39512106f +2 -0
- data/spec/fixtures/worktree/_git/objects/93/b714995d24c52180195876058a49c7d7fea0ad +0 -0
- data/spec/fixtures/worktree/_git/objects/96/25401ac3e19ef10868c140a76b719ac3f08fcf +0 -0
- data/spec/fixtures/worktree/_git/objects/b9/29ed2ab14f7489d5238a06d10d2f2c229a4ab4 +0 -0
- data/spec/fixtures/worktree/_git/objects/be/e0d26d33c284ee065e38bd7e81ae4bdc870f89 +4 -0
- data/spec/fixtures/worktree/_git/objects/c3/6491256978d26c08cd7aa97eee0f5631f96659 +0 -0
- data/spec/fixtures/worktree/_git/objects/d2/34c5e057fe32c676ea67e8cb38f4625ddaeb54 +0 -0
- data/spec/fixtures/worktree/_git/objects/df/6b0d2bcc76e6ec0fca20c227104a4f28bac41b +0 -0
- data/spec/fixtures/worktree/_git/objects/e5/b6d4317cefa946d77fc91539f1f1e48b60836f +0 -0
- data/spec/fixtures/worktree/_git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 +0 -0
- data/spec/fixtures/worktree/_git/objects/info/packs +2 -0
- data/spec/fixtures/worktree/_git/objects/pack/pack-f1c1717e9264c12310c7bd3e7dcdd28924000ff6.idx +0 -0
- data/spec/fixtures/worktree/_git/objects/pack/pack-f1c1717e9264c12310c7bd3e7dcdd28924000ff6.pack +0 -0
- data/spec/fixtures/worktree/_git/packed-refs +2 -0
- data/spec/fixtures/worktree/_git/refs/heads/branch-a +1 -0
- data/spec/fixtures/worktree/_git/refs/heads/branch-b +1 -0
- data/spec/fixtures/worktree/_git/refs/heads/master +1 -0
- data/spec/fixtures/worktree/_git/refs/tags/simple-tag +1 -0
- data/spec/fixtures/worktree/_git/refs/tags/test3-tag +1 -0
- data/spec/fixtures/worktree/sample-a.txt +1 -0
- data/spec/fixtures/worktree/sample.txt +1 -0
- data/spec/fixtures/worktree/subdir/sample-sub.txt +0 -0
- data/spec/git-object-browser/main_spec.rb +31 -0
- data/spec/git-object-browser/models/bindata_spec.rb +144 -0
- data/spec/git-object-browser/models/git_object_spec.rb +49 -0
- data/spec/git-object-browser/models/index_spec.rb +42 -0
- data/spec/git-object-browser/models/index_tree_extension_spec.rb +58 -0
- data/spec/git-object-browser/models/plain_file_spec.rb +11 -0
- data/spec/spec_helper.rb +19 -0
- metadata +260 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# https://github.com/git/git/blob/master/sha1_file.c
|
4
|
+
# https://github.com/git/git/blob/master/patch-delta.c
|
5
|
+
# https://github.com/git/git/blob/master/builtin/unpack-objects.c
|
6
|
+
# https://github.com/mojombo/grit/blob/master/lib/grit/git-ruby/internal/pack.rb
|
7
|
+
module GitObjectBrowser
|
8
|
+
|
9
|
+
module Models
|
10
|
+
|
11
|
+
class PackedObject < Bindata
|
12
|
+
|
13
|
+
attr_reader :header, :raw_data
|
14
|
+
attr_reader :object
|
15
|
+
|
16
|
+
TYPES = %w{
|
17
|
+
undefined
|
18
|
+
commit
|
19
|
+
tree
|
20
|
+
blob
|
21
|
+
tag
|
22
|
+
undefined
|
23
|
+
ofs_delta
|
24
|
+
ref_delta
|
25
|
+
}
|
26
|
+
|
27
|
+
def initialize(index, input)
|
28
|
+
super(input)
|
29
|
+
@index = index
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse(offset)
|
33
|
+
parse_raw(offset)
|
34
|
+
input = "#{ @object_type } #{ @object_size }\0" + @raw_data
|
35
|
+
@object = GitObject.new(nil).parse_inflated(input)
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse_raw(offset)
|
40
|
+
@offset = offset
|
41
|
+
@header = parse_header(offset)
|
42
|
+
|
43
|
+
if @header[:type] == 'ofs_delta'
|
44
|
+
obj_ofs_delta
|
45
|
+
elsif @header[:type] == 'ref_delta'
|
46
|
+
obj_ref_delta
|
47
|
+
else
|
48
|
+
@object_type = @header[:type]
|
49
|
+
@object_size = @header[:size]
|
50
|
+
@raw_data = zlib_inflate
|
51
|
+
end
|
52
|
+
|
53
|
+
[@object_type, @object_size]
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_header(offset)
|
57
|
+
seek(offset)
|
58
|
+
(type, size, header_size) = parse_type_and_size
|
59
|
+
type = TYPES[type]
|
60
|
+
{ :type => type, :size => size, :header_size => header_size }
|
61
|
+
end
|
62
|
+
|
63
|
+
def parse_ofs_delta_header(offset, header)
|
64
|
+
(delta_offset, delta_header_size) = parse_delta_offset
|
65
|
+
header[:base_offset] = offset - delta_offset
|
66
|
+
header[:header_size] += delta_header_size
|
67
|
+
header
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_ref_delta_header(header)
|
71
|
+
header[:base_sha1] = hex(20)
|
72
|
+
header[:header_size] += 20
|
73
|
+
header
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_hash
|
77
|
+
return {
|
78
|
+
:offset => @offset,
|
79
|
+
:type => @header[:type], # commit, tree, blob, tag, ofs_delta, ref_delta
|
80
|
+
:size => @header[:size],
|
81
|
+
:header_size => @header[:header_size],
|
82
|
+
:base_offset => @header[:base_offset],
|
83
|
+
:delta_commands => @delta_commands,
|
84
|
+
:base_size => @base_size,
|
85
|
+
:object => @object.to_hash,
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.path?(relpath)
|
90
|
+
return relpath =~ %r{\Aobjects/pack/pack-[0-9a-f]{40}.pack\z}
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def obj_ofs_delta
|
96
|
+
parse_ofs_delta_header(@offset, @header)
|
97
|
+
load_base_and_patch_delta
|
98
|
+
end
|
99
|
+
|
100
|
+
def obj_ref_delta
|
101
|
+
parse_ref_delta_header(@header)
|
102
|
+
index_entry = @index.find(@header[:base_sha1])
|
103
|
+
@header[:base_offset] = index_entry[:offset]
|
104
|
+
load_base_and_patch_delta
|
105
|
+
end
|
106
|
+
|
107
|
+
def load_base_and_patch_delta
|
108
|
+
begin
|
109
|
+
pack = PackedObject.new(@index, @in)
|
110
|
+
(@object_type, _) = pack.parse_raw(@header[:base_offset])
|
111
|
+
@base = pack.raw_data
|
112
|
+
ensure
|
113
|
+
seek(@offset + @header[:header_size])
|
114
|
+
end
|
115
|
+
|
116
|
+
switch_source(StringIO.new(zlib_inflate)) { patch_delta }
|
117
|
+
end
|
118
|
+
|
119
|
+
def patch_delta
|
120
|
+
@base_size = parse_size
|
121
|
+
if @base.size != @base_size
|
122
|
+
raise 'incollect base size'
|
123
|
+
end
|
124
|
+
|
125
|
+
@object_size = parse_size
|
126
|
+
@delta_commands = []
|
127
|
+
@raw_data = ''
|
128
|
+
while ! @in.eof?
|
129
|
+
delta_command
|
130
|
+
end
|
131
|
+
if @object_size != @raw_data.size
|
132
|
+
raise 'incollect delta size'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def delta_command
|
137
|
+
cmd = byte
|
138
|
+
data = nil
|
139
|
+
if cmd & 0b10000000 != 0
|
140
|
+
(offset, size) = parse_base_offset_and_size(cmd)
|
141
|
+
data = @base[offset, size]
|
142
|
+
@raw_data << data
|
143
|
+
@delta_commands << { :source => :base, :offset => offset, :size => size }
|
144
|
+
elsif cmd != 0
|
145
|
+
size = cmd
|
146
|
+
data = raw(size)
|
147
|
+
@raw_data << data
|
148
|
+
@delta_commands << { :source => :delta, :size => size }
|
149
|
+
else
|
150
|
+
raise 'delta command is 0'
|
151
|
+
end
|
152
|
+
@delta_commands.last[:data] = shorten_utf8(data, 2000)
|
153
|
+
end
|
154
|
+
|
155
|
+
def shorten_utf8(bin, length)
|
156
|
+
str = bin.force_encoding('UTF-8')
|
157
|
+
str = '(not UTF-8)' unless str.valid_encoding?
|
158
|
+
str = str[0, length] + '...' if str.length > length
|
159
|
+
str
|
160
|
+
end
|
161
|
+
|
162
|
+
def zlib_inflate
|
163
|
+
store = Zlib::Inflate.new
|
164
|
+
buffer = ''
|
165
|
+
while buffer.size < @header[:size]
|
166
|
+
rawdata = raw(4096)
|
167
|
+
if rawdata.size == 0
|
168
|
+
raise 'inflate error'
|
169
|
+
end
|
170
|
+
buffer << store.inflate(rawdata)
|
171
|
+
end
|
172
|
+
store.close
|
173
|
+
buffer
|
174
|
+
end
|
175
|
+
|
176
|
+
# sha1_file.c unpack_object_header_buffer
|
177
|
+
# unpack-objects.c unpack_one
|
178
|
+
def parse_type_and_size
|
179
|
+
hdr = byte
|
180
|
+
hdr_size = 1
|
181
|
+
continue = (hdr & 0b10000000)
|
182
|
+
type = (hdr & 0b01110000) >> 4
|
183
|
+
size = (hdr & 0b00001111)
|
184
|
+
size_len = 4
|
185
|
+
while continue != 0
|
186
|
+
hdr = byte
|
187
|
+
hdr_size += 1
|
188
|
+
continue = (hdr & 0b10000000)
|
189
|
+
size += (hdr & 0b01111111) << size_len
|
190
|
+
size_len += 7
|
191
|
+
end
|
192
|
+
return [type, size, hdr_size]
|
193
|
+
end
|
194
|
+
|
195
|
+
# delta.h get_delta_hdr_size
|
196
|
+
def parse_size
|
197
|
+
size = 0
|
198
|
+
size_len = 0
|
199
|
+
begin
|
200
|
+
hdr = byte
|
201
|
+
continue = (hdr & 0b10000000)
|
202
|
+
size += (hdr & 0b01111111) << size_len
|
203
|
+
size_len += 7
|
204
|
+
end while continue != 0
|
205
|
+
return size
|
206
|
+
end
|
207
|
+
|
208
|
+
# sha1_file.c get_delta_base
|
209
|
+
# unpack-objects.c unpack_delta_entry
|
210
|
+
def parse_delta_offset
|
211
|
+
offset = -1
|
212
|
+
hdr_size = 0
|
213
|
+
begin
|
214
|
+
hdr = byte
|
215
|
+
hdr_size += 1
|
216
|
+
continue = hdr & 0b10000000
|
217
|
+
low_offset = hdr & 0b01111111
|
218
|
+
offset = ((offset + 1) << 7) | low_offset
|
219
|
+
end while continue != 0
|
220
|
+
return [offset, hdr_size]
|
221
|
+
end
|
222
|
+
|
223
|
+
# patch-delta.c
|
224
|
+
def parse_base_offset_and_size(cmd)
|
225
|
+
offset = size = 0
|
226
|
+
offset = byte if cmd & 0b00000001 != 0
|
227
|
+
offset |= byte << 8 if cmd & 0b00000010 != 0
|
228
|
+
offset |= byte << 16 if cmd & 0b00000100 != 0
|
229
|
+
offset |= byte << 24 if cmd & 0b00001000 != 0
|
230
|
+
size = byte if cmd & 0b00010000 != 0
|
231
|
+
size |= byte << 8 if cmd & 0b00100000 != 0
|
232
|
+
size |= byte << 16 if cmd & 0b01000000 != 0
|
233
|
+
size = 0x10000 if size == 0
|
234
|
+
return [offset, size]
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
|
2
|
+
module GitObjectBrowser
|
3
|
+
|
4
|
+
module Models
|
5
|
+
|
6
|
+
class PackedRefs
|
7
|
+
def initialize(input)
|
8
|
+
@entries = []
|
9
|
+
while (line = input.gets) do
|
10
|
+
next if line =~ /\A\s*#/
|
11
|
+
next unless line =~ /(\^)?([0-9a-f]{40})\s*(.*)/
|
12
|
+
sha1 = $2
|
13
|
+
ref = $3
|
14
|
+
if $1
|
15
|
+
entry = @entries.last
|
16
|
+
entry[:tag_sha1] = sha1 if entry
|
17
|
+
else
|
18
|
+
entry = {}
|
19
|
+
entry[:sha1] = sha1
|
20
|
+
entry[:ref] = ref
|
21
|
+
@entries << entry
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_hash
|
27
|
+
return {
|
28
|
+
:entries => @entries
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.path?(relpath)
|
33
|
+
return relpath == "packed-refs"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
module GitObjectBrowser
|
3
|
+
|
4
|
+
module Models
|
5
|
+
|
6
|
+
class PlainFile
|
7
|
+
def initialize(input)
|
8
|
+
@in = input
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse
|
12
|
+
@content = @in.read(nil)
|
13
|
+
@content = @content.force_encoding('UTF-8')
|
14
|
+
@content = '(not UTF-8)' unless @content.valid_encoding?
|
15
|
+
@content = @content[0, 3000] + "\n..." if @content.length > 3000
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hash
|
20
|
+
return { 'content' => @content }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module GitObjectBrowser
|
3
|
+
|
4
|
+
module Models
|
5
|
+
|
6
|
+
class Ref
|
7
|
+
def initialize(input)
|
8
|
+
line = input.gets.to_s
|
9
|
+
|
10
|
+
if line =~ %r{\Aref:\s*(.+)}
|
11
|
+
@ref = $1
|
12
|
+
elsif line =~ %r{\A([0-9a-f]{40})}
|
13
|
+
@sha1 = $1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_hash
|
18
|
+
return {
|
19
|
+
"ref" => @ref,
|
20
|
+
"sha1" => @sha1
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.path?(relpath)
|
25
|
+
ref_names = %w{HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD CHERRY_PICK_HEAD}
|
26
|
+
return (ref_names.include?(relpath) || relpath =~ %r{\Arefs/})
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module GitObjectBrowser
|
4
|
+
|
5
|
+
class ObjectDumper
|
6
|
+
|
7
|
+
def initialize(input, output)
|
8
|
+
@object = Models::GitObject.new(input)
|
9
|
+
@out = output
|
10
|
+
end
|
11
|
+
|
12
|
+
def dump
|
13
|
+
@out << "type: #{@object.type}\n"
|
14
|
+
@out << "size: #{@object.size}\n"
|
15
|
+
@out << "sha1: #{@object.sha1}\n"
|
16
|
+
@out << "\n"
|
17
|
+
if @object.type == "tree"
|
18
|
+
dump_tree_entries
|
19
|
+
else
|
20
|
+
@out << @object.contents
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# man git-ls-tree
|
25
|
+
def dump_tree_entries
|
26
|
+
@object.entries.each do |entry|
|
27
|
+
@out << "#{entry[:mode]} #{entry[:sha1]} #{entry[:filename]}\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
|
2
|
+
module GitObjectBrowser
|
3
|
+
|
4
|
+
module Server
|
5
|
+
|
6
|
+
class GitServlet < WEBrick::HTTPServlet::AbstractServlet
|
7
|
+
def initialize(server, target)
|
8
|
+
@target = File.expand_path(target)
|
9
|
+
end
|
10
|
+
|
11
|
+
def do_GET(request, response)
|
12
|
+
# status, content_type, body = do_stuff_with request
|
13
|
+
path = request.path
|
14
|
+
unless path =~ %r{/.git(?:/(.*))?}
|
15
|
+
not_found(response)
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
@relpath = $1.to_s
|
20
|
+
path = File.join(@target, @relpath)
|
21
|
+
unless File.exist?(path)
|
22
|
+
not_found(response) unless redirect_to_packed_object(response)
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
if path =~ /\.\./
|
27
|
+
not_found(response)
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
return if response_directory(response)
|
32
|
+
return if response_index(response)
|
33
|
+
return if response_object(response)
|
34
|
+
return if response_ref(response)
|
35
|
+
return if response_packed_object(response, request.query["offset"])
|
36
|
+
return if response_pack_file(response)
|
37
|
+
return if response_pack_index(response)
|
38
|
+
return if response_info_refs(response)
|
39
|
+
return if response_packed_refs(response)
|
40
|
+
|
41
|
+
response_file(response)
|
42
|
+
end
|
43
|
+
|
44
|
+
def redirect_to_packed_object(response)
|
45
|
+
return false unless GitObjectBrowser::Models::GitObject.path?(@relpath)
|
46
|
+
sha1 = @relpath.gsub(%r{\A.*/([0-9a-f]{2})/([0-9a-f]{38})\z}, '\1\2')
|
47
|
+
|
48
|
+
Dir.chdir(@target) do
|
49
|
+
Dir.glob('objects/pack/*.idx') do |path|
|
50
|
+
File.open(path) do |input|
|
51
|
+
index = GitObjectBrowser::Models::PackIndex.new(input)
|
52
|
+
result = index.find(sha1)
|
53
|
+
unless result.nil?
|
54
|
+
packfile = path.sub(/\.idx\z/, '.pack')
|
55
|
+
response.status = 302
|
56
|
+
response["Location"] = "/.git/#{ packfile }?offset=#{ result[:offset] }"
|
57
|
+
return true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
false
|
63
|
+
end
|
64
|
+
|
65
|
+
def response_wrapped_object(response, type, obj)
|
66
|
+
ok(response)
|
67
|
+
hash = {}
|
68
|
+
hash["type"] = type
|
69
|
+
hash["object"] = obj.to_hash
|
70
|
+
hash["root"] = @target
|
71
|
+
hash["path"] = @relpath
|
72
|
+
hash["wroking_dir"] = File.basename(File.dirname(@target))
|
73
|
+
|
74
|
+
response.body = ::JSON.generate(hash)
|
75
|
+
end
|
76
|
+
|
77
|
+
def response_directory(response)
|
78
|
+
return false unless File.directory?(File.join(@target, @relpath))
|
79
|
+
|
80
|
+
obj = GitObjectBrowser::Models::Directory.new(@target, @relpath)
|
81
|
+
response_wrapped_object(response, "directory", obj)
|
82
|
+
return true
|
83
|
+
end
|
84
|
+
|
85
|
+
def response_index(response)
|
86
|
+
return false unless @relpath == "index"
|
87
|
+
|
88
|
+
obj = {}
|
89
|
+
File.open(File.join(@target, @relpath)) do |input|
|
90
|
+
obj = GitObjectBrowser::Models::Index.new(input).parse
|
91
|
+
end
|
92
|
+
response_wrapped_object(response, "index", obj)
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
|
96
|
+
def response_object(response)
|
97
|
+
return false unless GitObjectBrowser::Models::GitObject.path?(@relpath)
|
98
|
+
|
99
|
+
obj = {}
|
100
|
+
File.open(File.join(@target, @relpath)) do |input|
|
101
|
+
obj = GitObjectBrowser::Models::GitObject.new(input).parse
|
102
|
+
end
|
103
|
+
response_wrapped_object(response, "object", obj)
|
104
|
+
return true
|
105
|
+
end
|
106
|
+
|
107
|
+
def response_ref(response)
|
108
|
+
return false unless GitObjectBrowser::Models::Ref.path?(@relpath)
|
109
|
+
|
110
|
+
obj = {}
|
111
|
+
File.open(File.join(@target, @relpath)) do |input|
|
112
|
+
obj = GitObjectBrowser::Models::Ref.new(input)
|
113
|
+
end
|
114
|
+
response_wrapped_object(response, "ref", obj)
|
115
|
+
return true
|
116
|
+
end
|
117
|
+
|
118
|
+
def response_pack_index(response)
|
119
|
+
return false unless GitObjectBrowser::Models::PackIndex.path?(@relpath)
|
120
|
+
obj = {}
|
121
|
+
File.open(File.join(@target, @relpath)) do |input|
|
122
|
+
obj = GitObjectBrowser::Models::PackIndex.new(input).parse
|
123
|
+
end
|
124
|
+
File.open(index_to_pack_path) do |input|
|
125
|
+
obj.load_object_types(input)
|
126
|
+
end
|
127
|
+
response_wrapped_object(response, "pack_index", obj)
|
128
|
+
return true
|
129
|
+
end
|
130
|
+
|
131
|
+
def response_pack_file(response)
|
132
|
+
return false unless GitObjectBrowser::Models::PackFile.path?(@relpath)
|
133
|
+
obj = {}
|
134
|
+
File.open(File.join(@target, @relpath)) do |input|
|
135
|
+
obj = GitObjectBrowser::Models::PackFile.new(input).parse
|
136
|
+
end
|
137
|
+
response_wrapped_object(response, 'pack_file', obj)
|
138
|
+
return true
|
139
|
+
end
|
140
|
+
|
141
|
+
def index_to_pack_path
|
142
|
+
File.join(@target, @relpath.sub(/\.idx\z/, '.pack'))
|
143
|
+
end
|
144
|
+
|
145
|
+
def pack_to_index_path
|
146
|
+
File.join(@target, @relpath.sub(/\.pack\z/, '.idx'))
|
147
|
+
end
|
148
|
+
|
149
|
+
def response_packed_object(response, offset)
|
150
|
+
return false if offset.nil?
|
151
|
+
return false unless GitObjectBrowser::Models::PackedObject.path?(@relpath)
|
152
|
+
obj = {}
|
153
|
+
|
154
|
+
File.open(pack_to_index_path) do |index_input|
|
155
|
+
index = GitObjectBrowser::Models::PackIndex.new(index_input)
|
156
|
+
File.open(File.join(@target, @relpath)) do |input|
|
157
|
+
obj = GitObjectBrowser::Models::PackedObject.new(index, input).parse(offset.to_i)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
response_wrapped_object(response, "packed_object", obj)
|
161
|
+
return true
|
162
|
+
end
|
163
|
+
|
164
|
+
def response_info_refs(response)
|
165
|
+
return false unless GitObjectBrowser::Models::InfoRefs.path?(@relpath)
|
166
|
+
|
167
|
+
obj = {}
|
168
|
+
File.open(File.join(@target, @relpath)) do |input|
|
169
|
+
obj = GitObjectBrowser::Models::InfoRefs.new(input)
|
170
|
+
end
|
171
|
+
response_wrapped_object(response, "info_refs", obj)
|
172
|
+
return true
|
173
|
+
end
|
174
|
+
|
175
|
+
def response_packed_refs(response)
|
176
|
+
return false unless GitObjectBrowser::Models::PackedRefs.path?(@relpath)
|
177
|
+
|
178
|
+
obj = {}
|
179
|
+
File.open(File.join(@target, @relpath)) do |input|
|
180
|
+
obj = GitObjectBrowser::Models::PackedRefs.new(input)
|
181
|
+
end
|
182
|
+
response_wrapped_object(response, "packed_refs", obj)
|
183
|
+
return true
|
184
|
+
end
|
185
|
+
|
186
|
+
def response_file(response)
|
187
|
+
path = File.join(@target, @relpath)
|
188
|
+
obj = {}
|
189
|
+
File.open(path) do |input|
|
190
|
+
obj = GitObjectBrowser::Models::PlainFile.new(input).parse
|
191
|
+
end
|
192
|
+
response_wrapped_object(response, "file", obj)
|
193
|
+
return true
|
194
|
+
end
|
195
|
+
|
196
|
+
def ok(response)
|
197
|
+
response.status = 200
|
198
|
+
response['Content-Type'] = 'application/json'
|
199
|
+
end
|
200
|
+
|
201
|
+
def not_found(response)
|
202
|
+
response.status = 404
|
203
|
+
response['Content-Type'] = 'application/json'
|
204
|
+
response.body = '{}'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|