wsz
2025-06-10 70eedd3bf9810bc022967da6446dd8a0ff6f43b7
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
local util = require 'git.util'
 
local assert, next, io, print, os, type, string, pairs, tostring =
    assert, next, io, print, os, type, string, pairs, tostring
local join_path = git.util.join_path
 
local require = require
 
local isPosix = package.config:sub(1,1) == '/' -- wild guess
 
module(...)
 
Commit = {}
Commit.__index = Commit
 
function Commit:tree()
    return self.repo:tree(self.tree_sha)
end
 
function Commit:checkout(path)
    assert(path, 'path argument missing')
    self:tree():checkoutTo(path)
end
 
 
Tree = {}
Tree.__index = function (t,k)
    if Tree[k] then return Tree[k] end
    return t:entry(k)
end
 
function Tree:entries()
    return function(t, n)
        local n, entry = next(t, n)
        if entry then
            local object
            if entry.type == 'tree' then
                object = self.repo:tree(entry.id)
            elseif entry.type == 'blob' then
                object = self.repo:blob(entry.id)
                object.mode = entry.mode
            elseif entry.type == 'commit' then
                -- this is possibly a commit in a submodule, 
                -- do not retrieve it from current repo
                object = entry
            else
                error('Unknown entry type: ' .. entry.type)
            end
            return n, entry.type, object
        end
    end, self._entries
end
 
function Tree:entry(n)
    local e = self._entries[n]
    if not e then return end
    if e.type == 'tree' then
        return self.repo:tree(e.id)
    elseif e.type == 'commit' then
        return self.repo:commit(e.id)
    elseif e.type == 'blob' then
        return self.repo:blob(e.id)
    else
        error('Unknown entry type: ' .. e.type)
    end
end
 
function Tree:walk(func, path)
    path = path or '.'
    assert(type(func) == "function", "argument is not a function")
    local function walk(tree, path)
        for name, type, entry in tree:entries() do
            local entry_path = join_path(path, name)
            func(entry, entry_path, type)
 
            if type == "tree" then
                walk(entry, entry_path)
            end
        end
    end
    walk(self, path)
end
 
function Tree:checkoutTo(path)
    util.make_dir(path)
    self:walk(function (entry, entry_path, type)
        if type == 'tree' then
            util.make_dir(entry_path)
        elseif type == 'blob' then
            local out = assert(io.open(entry_path, 'wb'))
            out:write(entry:content())
            out:close()
            if isPosix then
                local mode = entry.mode:sub(-3,-1) -- fixme: is this ok?
                local cmd = 'chmod '..mode..' "'..entry_path..'"'
                os.execute(cmd)
            end
        elseif type == 'commit' then
            -- this is a submodule referencing a commit,
            -- make a directory for it
            util.make_dir(entry_path)
        else
            error('Unknown entry type: ', type)
        end
    end, path)
end
 
Blob = {}
Blob.__index = Blob
 
function Blob:content()
    if self.stored then
        local f = self.repo:raw_object(self.id)
        local ret = f:read('*a') or ""
        f:close()
        return ret
    else
        return self.data
    end
end