Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

can now parse basic packfile data, but not deltas yet

  • Loading branch information...
commit d95230d21b1f6cfcc601cc13047bb25d174ca2b9 1 parent 2e46f3f
@schacon authored
Showing with 88 additions and 25 deletions.
  1. +16 −23 src/git.erl
  2. +68 −0 src/packfile.erl
  3. +4 −2 t/002-cat-file-packed.t
View
39 src/git.erl
@@ -30,29 +30,12 @@ print_log(Git, Ref) ->
% traverse the reference, printing out all the log information to stdout
io:fwrite("Log:~n").
-read_object(Git, ObjectSha) ->
- RawData = get_object_data(Git, ObjectSha),
- extract_object_data(RawData).
-
-
-%% TODO: make this more efficient - this is ridiculous
-%% should be able to do this as a binary
-extract_object_data(CompData) ->
- RawData = binary_to_list(zlib:uncompress(CompData)),
- Split = string:chr(RawData, 0),
- {Header, Data} = lists:split(Split, RawData),
- Split2 = string:chr(Header, 32),
- Header2 = lists:sublist(Header, length(Header) - 1),
- {Type, Size} = lists:split(Split2, Header2),
- Type2 = lists:sublist(Type, length(Type) - 1),
- {binary_to_atom(list_to_binary(Type2), latin1), list_to_integer(Size), list_to_binary(Data)}.
-
git_dir(Git) ->
{Path} = Git,
Path.
% get the raw object data out of loose or packed formats
-get_object_data(Git, ObjectSha) ->
+read_object(Git, ObjectSha) ->
% see if the object is loose, read the data
% else check the packfile indexes and get the object out of a packfile
First = string:substr(ObjectSha, 1, 2),
@@ -60,12 +43,25 @@ get_object_data(Git, ObjectSha) ->
FileName = git_dir(Git) ++ "/objects/" ++ First ++ "/" ++ Second,
case file:read_file(FileName) of
{ok, Data} ->
- Data;
+ extract_loose_object_data(Data);
_Else ->
get_packfile_object_data(Git, ObjectSha)
end.
+%% TODO: make this more efficient - this is ridiculous
+%% should be able to do this as a binary
+extract_loose_object_data(CompData) ->
+ RawData = binary_to_list(zlib:uncompress(CompData)),
+ Split = string:chr(RawData, 0),
+ {Header, Data} = lists:split(Split, RawData),
+ Split2 = string:chr(Header, 32),
+ Header2 = lists:sublist(Header, length(Header) - 1),
+ {Type, Size} = lists:split(Split2, Header2),
+ Type2 = lists:sublist(Type, length(Type) - 1),
+ {binary_to_atom(list_to_binary(Type2), latin1), list_to_integer(Size), list_to_binary(Data)}.
+
get_packfile_object_data(Git, ObjectSha) ->
+ io:fwrite("SHA:~p~n", [ObjectSha]),
PackIndex = git_dir(Git) ++ "/objects/pack",
case file:list_dir(PackIndex) of
{ok, Filenames} ->
@@ -79,10 +75,7 @@ get_packfile_object_data(Git, ObjectSha) ->
end;
_Else ->
invalid
- end,
- FileName = git_dir(Git) ++ "/objects/8d/47f3435ce5dfd0b2ab5758590c2db21b5294b4",
- {ok, Data} = file:read_file(FileName),
- Data.
+ end.
get_packfile_with_object(Git, [Index|Rest], ObjectSha) ->
PackIndex = git_dir(Git) ++ "/objects/pack/" ++ Index,
View
68 src/packfile.erl
@@ -0,0 +1,68 @@
+%%
+%% Partial Git Implementation
+%%
+
+-module(packfile).
+-export([get_packfile_data/2]).
+
+get_packfile_data(PackFilePath, Offset) ->
+ case file:open(PackFilePath, [read, binary]) of
+ {ok, IoDevice} ->
+ file:position(IoDevice, Offset),
+ {ok, Byte} = file:read(IoDevice, 1),
+ <<ContinueBit:1, Type:3, InitSize:4>> = Byte,
+ %io:fwrite("Object Data: ~p ~p ~p~n", [ContinueBit, Type, InitSize]),
+ {Size, Data} = read_object_data(IoDevice, ContinueBit, InitSize, 0),
+ %io:fwrite("Final Object Data: ~p ~p ~p~n", [Size, Type, Data]),
+ {type_int_to_term(Type), Size, Data};
+ {error, Reason} ->
+ error
+ end.
+
+read_object_data(IoDevice, 1, Size, Offset) ->
+ {ok, Byte} = file:read(IoDevice, 1),
+ <<ContinueBit:1, NextSize:7>> = Byte,
+ % bit shift the size
+ NextOffset = Offset + 4,
+ SizeOr = NextSize bsl NextOffset,
+ NewSize = Size bor SizeOr,
+ %io:fwrite("Object Data: ~p ~p (~p)~n", [ContinueBit, NextSize, NewSize]),
+ read_object_data(IoDevice, ContinueBit, NewSize, NextOffset);
+read_object_data(IoDevice, 0, Size, Offset) ->
+ Z = zlib:open(),
+ ok = zlib:inflateInit(Z),
+ Data = inflate_object_data(Z, IoDevice, []),
+ %io:fwrite("Bits:~p~n", [Data]),
+ ok = zlib:inflateEnd(Z),
+ zlib:close(Z),
+ {Size, Data}.
+
+inflate_object_data(Z, IoDevice, SoFar) ->
+ case file:read(IoDevice, 4096) of
+ {ok, Bytes} ->
+ Inflated = case catch zlib:inflate(Z, Bytes) of
+ {'EXIT', {'data_error', _Backtrace} } ->
+ io:format("zlib:inflate data_error,~n"),
+ SoFar;
+ {'EXIT', Reason} ->
+ io:format("zlib:inflate error -> [~p]~n", [Reason]),
+ SoFar;
+ Iolist ->
+ [Data] = Iolist,
+ ListData = list_to_binary([binary_to_list(Data)|SoFar]),
+ io:fwrite("Size: ~p~n", [size(ListData)]),
+ inflate_object_data(Z, IoDevice, ListData)
+ end;
+ _Else ->
+ SoFar
+ end.
+
+type_int_to_term(ObjInt) ->
+ case ObjInt of
+ 1 -> commit;
+ 2 -> tree;
+ 3 -> blob;
+ 4 -> tag;
+ 6 -> delta_ofs;
+ 7 -> delta_base
+ end.
View
6 t/002-cat-file-packed.t
@@ -4,6 +4,8 @@
main(_) ->
Git = git:open("test_git"),
- {Type, Size, Data} = git:read_object(Git, "aa7dfe7c2a634cb9e7a9d5838eb58fe150ebd7fb"), %% tree
+ %{Type, Size, Data} = git:read_object(Git, "aa7dfe7c2a634cb9e7a9d5838eb58fe150ebd7fb"), %% tree
+ {Type, Size, Data} = git:read_object(Git, "be62addb149d286893e2ec254e0dc783a871e8af"), %% tree
io:fwrite("Data: ~p ~p~n", [Type, Size]),
- io:fwrite("Data: ~p~n", [Data]).
+ io:fwrite("Data: ~p~n", [Data]),
+ io:fwrite("Data: ~s~n", [binary_to_list(Data)]).
Please sign in to comment.
Something went wrong with that request. Please try again.