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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
-- gall.ll
--
-- Git Abstraction Layer for Lua -- Low level interface
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
--
---
-- Low level interface to Git
--
-- In addition to the documented functions below, the following Git subcommands
-- are directly exposed as functions which are essentially like...
--
-- function gall.ll.FUNC(...)
-- return gall.ll.rungit("FUNC", ...)
-- end
--
-- ...but with correct handling of the tabular input to @{rungit}
--
-- The current full-list of those functions is:
--
-- * cat_file exposes `git cat-file`
-- * symbolic_ref exposes `git symbolic-ref`
-- * show_ref exposes `git show-ref`
-- * hash_object exposes `git hash-object`
-- * ls_tree exposes `git ls-tree`
-- * init exposes `git init`
-- * merge_base exposes `git merge-base`
-- * rev_list exposes `git rev-list`
-- * config exposes `git config`
--
-- @module gall.ll
local sp = require "luxio.subprocess"
local util = require "gall.util"
local git2
if os.getenv "GALL_DISABLE_GIT2" then
git2 = nil
else
ok, git2 = pcall(require, "gall.ll.git2")
if not ok then
git2 = nil
end
end
local assert = assert
local git_exe = "git"
---
-- Run the given git process.
--
-- The run parameters must contain:
--
-- * The `repo` (the value for `GIT_DIR`)
-- * At least 1 list entry for the command line
-- * Optionally an `env` table.
-- * Optionally a string `stdin` to pass into the process
-- * Optionally a `stdout` boolean which says whether to capture stdout
-- * Optionally a `stderr` boolean which says whether to capture stderr
--
-- Additionally, if `stdout` or `stderr` are functions instead of booleans then
-- they will be called to filter the stream after reading.
--
-- @function rungit
-- @tparam table t Run parameters
-- @treturn number Exit code
-- @treturn string Contents of stdout
-- @treturn string Contents of stderr
-- @raise Function will assert if given no repository or if the subprocess is killed uncleanly
local function _rungit(t)
assert(t.repo, "No repository?")
local proc_args = {
env = {},
git_exe, unpack(t)
}
for k, v in pairs(t.env or {}) do
proc_args.env[k] = v
end
proc_args.env.GIT_DIR = t.repo
if t.stdin then
proc_args.stdin = t.stdin
end
if t.stdout then
proc_args.stdout = sp.PIPE
end
if t.stderr then
proc_args.stderr = sp.PIPE
end
local proc = sp.spawn_simple(proc_args)
local stdout, stderr
if t.stdout then
stdout = proc.stdout:read("*a")
proc.stdout:close()
if type(t.stdout) == "function" then
stdout = t.stdout(stdout)
end
end
if t.stderr then
stderr = proc.stderr:read("*a")
proc.stderr:close()
if type(t.stderr) == "function" then
stderr = t.stderr(stderr)
end
end
local how, why = proc:wait()
assert(how == "exit", "Not cleanly exited")
return why, stdout, stderr
end
---
-- Get / Set the git executable
--
-- If `e` is provided, then it is set as the path, otherwise the current
-- path is returned as-is. By default, the Git executable is simply `git`.
--
-- @function get_set_git
-- @tparam ?string e The path to the Git executable
-- @treturn string The path to the Git executable
local function get_set_git(e)
if e then
git_exe = e
end
return git_exe
end
---
-- Remove a trailing newline if present.
--
-- @function chomp
-- @tparam string s The string to chomp
-- @treturn string `s` with a single trailing newline (if present) removed.
local function _chomp(s)
local rest = s:match("^(.*)\n$")
return rest or s
end
local mod_ret = {
rungit = _rungit,
get_set_git = get_set_git,
chomp = _chomp,
git2 = git2,
}
local simple_cmds = {
"cat-file", "symbolic-ref", "show-ref",
"hash-object", "ls-tree", "init", "merge-base", "rev-list", "config"
}
for _, s in pairs(simple_cmds) do
local ss = s:gsub("%-", "_")
local function ss_fn(_t)
local t = util.deep_copy(_t)
table.insert(t, 1, s)
t.stdout = _chomp
return _rungit(t)
end
mod_ret[ss] = ss_fn
end
return mod_ret
|