forked from SWI-Prolog/swish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit_export.pl
146 lines (126 loc) · 4.58 KB
/
git_export.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
:- module(git_export, [
git_export/1 % +GitDir, export gitty to bare git repo in GitDir
]).
:- use_module(lib/storage).
:- use_module(library(git)).
:- use_module(library(filesex)).
git_export(Target) :-
git_init(Target),
gitty_heads(Heads),
find_all_commits(Heads, Commits0),
keysort(Commits0, Commits),
commits_to_git(init, init, Commits, Tip, [directory(Target)]),
catch(git(['update-ref', 'refs/heads/master', Tip],
[directory(Target)]), _, fail).
gitty_heads(Heads) :-
findall(Commit,
( storage_file(_File, _Data, Meta),
get_dict(commit, Meta, Commit)
),
Heads).
author_url(X,X).
author_email(_X, '[email protected]').
find_author(Commit, Author) :-
get_dict(author, Commit, Author),
!.
find_author(Commit, Author) :-
get_dict(previous, Commit, Prev),
storage_file(Prev, _, Meta),
find_author(Meta, Author),
!.
find_author(_, anonymous).
git_init(Target) :-
exists_directory(Target),
!.
git_init(Target) :-
make_directory_path(Target),
git([init, '--bare'], [directory(Target)]).
find_all_commits([], []).
find_all_commits([H|T], Commits) :-
find_all_previous_commits(H, HeadCommits),
find_all_commits(T, TailCommits),
append(HeadCommits, TailCommits, Commits).
find_all_previous_commits(C, Commits) :-
storage_file(C, Data, Meta),
( get_dict(previous, Meta, Prev)
-> find_all_previous_commits(Prev, PrevCommits)
; PrevCommits = []
),
Meta1 = Meta.put(content,Data),
Commits = [Meta.time-Meta1|PrevCommits].
store_git_object(Meta, Hash, Options) :-
option(type(Type), Options, blob),
( Type == tree
-> Encoding = binary
; Encoding = utf8
),
tmp_file_stream(Encoding, Tmp, Stream),
write(Stream, Meta.content), close(Stream),
catch(git(['hash-object', '-w', '-t', Type, Tmp]
,[output(Codes)|Options]), _, fail),
atom_codes(HashN, Codes),
sub_atom(HashN, 0, _, 1, Hash). % remove trailing new line ...
commits_to_git(_,Head,[], Head, _Options) :- !.
commits_to_git(init, init, [_-H|T], Tip, Options) :-
!,
store_blob(H, Options),
update_tree(H, tree{}, Tree, TreeContent, Options),
store_commit(H, Tree, init, Hash, Options),
commits_to_git(TreeContent, Hash, T, Tip, Options).
commits_to_git(TreeContent, GitParent, [_-H|T], Tip, Options) :-
!,
store_blob(H, Options),
update_tree(H, TreeContent, Tree, NewTreeContent, Options),
store_commit(H, Tree, GitParent, Hash, Options),
commits_to_git(NewTreeContent, Hash, T, Tip, Options).
update_tree(Meta, OldContent, TreeHash, NewContent, Options) :-
gv_hash_atom(Codes, Meta.data),
atom_codes(HashCode,Codes),
format(atom(Hdr), '100644 ~w\u0000', [Meta.name]),
atom_concat(Hdr, HashCode, A),
put_dict(Meta.name, OldContent, A, NewContent),
treedict_treecontent(NewContent, TreeContent),
store_git_object(_{content:TreeContent}, TreeHash, [type(tree)|Options]).
treedict_treecontent(D, C) :-
findall(Value, get_dict(_Key, D, Value), L),
atomic_list_concat(L, C).
store_commit(H, TreeHash, GitParent, Hash, Options) :-
find_author(H, Author),
author_url(Author, AuthorURL), CommitterURL = AuthorURL,
author_email(Author, AuthorEmail), CommitterEmail = AuthorEmail,
format_time(atom(GitTimeStamp), '%s %z', H.time), % Git time format
( get_dict(commit_message, H, Comment) -> true; Comment = ''),
( GitParent = init
-> ParentLine = ''
; format(atom(ParentLine), 'parent ~w\n', [GitParent])
),
format(atom(CommitContent),
'tree ~w~n~wauthor ~w <~w> ~w~ncommitter ~w <~w> ~w~n~n~w~n',
[TreeHash, ParentLine,
AuthorURL, AuthorEmail, GitTimeStamp,
CommitterURL, CommitterEmail, GitTimeStamp,
Comment]),
store_git_object(_{content:CommitContent}, Hash, [type(commit)|Options]).
store_blob(H, Options) :-
store_git_object(H, _Hash, Options).
%% gv_hash_atom(+Codes, -Hash) is det.
% gv_hash_atom(-Codes, +Hash) is det.
%
% Bi-directional version of hash_atom/2 ...
%
gv_hash_atom(Codes, Hash) :-
nonvar(Codes),
!,
hash_atom(Codes, Hash).
gv_hash_atom(Codes, Hash) :-
nonvar(Hash),
atom_chars(Hash, Chars),
phrase(hex_bytes(Chars), Codes).
hex_bytes([High,Low|T]) -->
{ char_type(High, xdigit(H)),
char_type(Low, xdigit(L)),
Code is 16*H + L
},
[Code],
hex_bytes(T).
hex_bytes([]) --> [].