1+ const PathInfo = Union{String, Int64, Symbol}
2+
3+ struct GitLeaf
4+ mode:: String
5+ hash:: String
6+ end
7+
8+ struct GitTree
9+ children:: Dict{String, Union{GitTree, GitLeaf}}
10+ end
11+ GitTree() = GitTree(Dict{String, Union{GitTree, GitLeaf}}())
12+
113function iterate_headers(
214 callback:: Function ,
315 tar:: IO ;
168180
169181# resolve symlink target or nothing if not valid
170182function link_target(
171- paths:: Dict{String} ,
183+ paths:: Dict{String, PathInfo } ,
172184 path:: AbstractString ,
173185 link:: AbstractString ,
174186)
@@ -214,21 +226,21 @@ function git_tree_hash(
214226 buf:: Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
215227) where HashType <: SHA.SHA_CTX
216228 # build tree with leaves for files and symlinks
217- tree = Dict{String,Any} ()
229+ tree = GitTree ()
218230 read_tarball(predicate, tar; buf= buf) do hdr, parts
219231 isempty(parts) && return
220232 name = pop!(parts)
221233 node = tree
222234 for part in parts
223- node′ = get(node, part, nothing )
224- if ! (node′ isa Dict )
225- node′ = node[part] = Dict{String,Any} ()
235+ child = get(node. children , part, nothing )
236+ if ! (child isa GitTree )
237+ child = node. children [part] = GitTree ()
226238 end
227- node = node′
239+ node = child
228240 end
229241 if hdr. type == :directory
230- if ! (get(node, name, nothing ) isa Dict )
231- node[name] = Dict{String,Any} ()
242+ if ! (get(node. children , name, nothing ) isa GitTree )
243+ node. children [name] = GitTree ()
232244 end
233245 return
234246 elseif hdr. type == :symlink
@@ -238,47 +250,51 @@ function git_tree_hash(
238250 end
239251 elseif hdr. type == :hardlink
240252 mode = iszero(hdr. mode & 0o100 ) ? " 100644" : " 100755"
241- node′ = tree
253+ linked = tree
242254 for part in split(hdr. link, ' /' )
243- node′ = node′ [part]
255+ linked = linked . children [part]
244256 end
245- hash = node′[ 2 ] # hash of linked file
257+ hash = ( linked:: GitLeaf ) . hash
246258 elseif hdr. type == :file
247259 mode = iszero(hdr. mode & 0o100 ) ? " 100644" : " 100755"
248260 hash = git_file_hash(tar, hdr. size, HashType, buf= buf)
249261 else
250262 error(" unsupported type for git tree hashing: $(hdr. type) " )
251263 end
252- node[name] = (mode, hash)
264+ node. children [name] = GitLeaf (mode, hash)
253265 end
254266
255267 # prune directories that don't contain any files
256268 if skip_empty
257- prune_empty!(node:: Tuple ) = true
258- function prune_empty!(node:: Dict )
259- filter!(node) do (name, child)
260- prune_empty!(child)
261- end
262- return ! isempty(node)
263- end
264269 prune_empty!(tree)
265270 end
266271
267272 # reduce the tree to a single hash value
268- hash_tree(node:: Tuple ) = node
269- function hash_tree(node:: Dict )
270- by((name, child)) = child isa Dict ? " $name /" : name
271- hash = git_object_hash(" tree" , HashType) do io
272- for (name, child) in sort!(collect(node), by= by)
273- mode, hash = hash_tree(child)
274- print(io, mode, ' ' , name, ' \0 ' )
275- write(io, hex2bytes(hash))
276- end
277- end
278- return " 40000" , hash
273+ return hash_git_tree(tree, HashType)[end ]
274+ end
275+
276+ prune_empty!(node:: GitLeaf ) = true
277+ function prune_empty!(node:: GitTree )
278+ filter!(node. children) do (name, child)
279+ prune_empty!(child)
279280 end
281+ return ! isempty(node. children)
282+ end
280283
281- return hash_tree(tree)[end ]
284+ function hash_git_tree(node:: GitLeaf , :: Type{HashType} ) where HashType <: SHA.SHA_CTX
285+ return (node. mode, node. hash)
286+ end
287+
288+ function hash_git_tree(node:: GitTree , :: Type{HashType} ) where HashType <: SHA.SHA_CTX
289+ by((name, child)) = child isa GitTree ? " $name /" : name
290+ hash = git_object_hash(" tree" , HashType) do io
291+ for (name, child) in sort!(collect(node. children), by= by)
292+ mode, hash = hash_git_tree(child, HashType)
293+ print(io, mode, ' ' , name, ' \0 ' )
294+ write(io, hex2bytes(hash))
295+ end
296+ end
297+ return (" 40000" , hash)
282298end
283299
284300function git_object_hash(
@@ -350,7 +366,7 @@ function read_tarball(
350366)
351367 write_skeleton_header(skeleton, buf= buf)
352368 # symbols for path types except symlinks store the link
353- paths = Dict{String,Any }()
369+ paths = Dict{String, PathInfo }()
354370 globals = Dict{String,String}()
355371 while ! eof(tar)
356372 hdr = read_header(tar, globals= globals, buf= buf, tee= skeleton)
0 commit comments