summaryrefslogtreecommitdiff
path: root/tar-clone.lua
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@gmail.com>2013-11-05 20:47:12 +0000
committerRichard Maw <richard.maw@gmail.com>2013-11-05 20:48:00 +0000
commit2c852a412d923cf71b7747e91696f94ca711ae20 (patch)
treee6cdc56dff3d2bef457fe4ec248c394cc4900d0b /tar-clone.lua
parentb249e5de5119f25e872f5af4f93a810271339bbf (diff)
downloadtar-clone-2c852a412d923cf71b7747e91696f94ca711ae20.tar.bz2
Tidy up to be more useful as a stand-alone tool
Diffstat (limited to 'tar-clone.lua')
-rwxr-xr-xtar-clone.lua171
1 files changed, 171 insertions, 0 deletions
diff --git a/tar-clone.lua b/tar-clone.lua
new file mode 100755
index 0000000..f89ad26
--- /dev/null
+++ b/tar-clone.lua
@@ -0,0 +1,171 @@
+#!/usr/bin/lua
+local repo, localname = ...
+
+local luxio = require "luxio"
+local sio = require "luxio.simple"
+local sp = require "luxio.subprocess"
+local archive = require "archive"
+
+local CONFIG_FORMAT = [[
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[remote "origin"]
+ url = %s
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "%s"]
+ remote = origin
+ merge = refs/heads/%s
+]]
+local function get_config(fetch_url, HEAD)
+ return CONFIG_FORMAT:format(fetch_url, HEAD, HEAD)
+end
+
+
+local include_whitelist = {
+ "./objects",
+ "./description",
+}
+local function matches_prefix(path, prefix)
+ local matched = string.sub(path, 1, string.len(prefix)) == prefix
+ return matched
+end
+local function whitelisted(path)
+ for _, prefix in ipairs(include_whitelist) do
+ if matches_prefix(path, prefix) then
+ return true
+ end
+ end
+ return false
+end
+
+
+local function copy_file(from, write_all, buffer_size)
+ -- Default buffer size is 4M, but can be changed
+ buffer_size = buffer_size or 4 * 1024 * 1024
+ local fromfile, emsg = sio.open(from, "r")
+ if not fromfile then
+ return false, emsg
+ end
+ local write_count
+ repeat
+ local ok
+ local bytes, emsg = fromfile:read(buffer_size)
+ if not bytes then
+ fromfile:close()
+ tofile:close()
+ return false, emsg
+ end
+ ok, write_count, emsg = write_all(bytes)
+ if not ok then
+ fromfile:close()
+ return false, emsg
+ end
+ until write_count == 0
+ return true
+end
+
+
+local function add_dir(writer, basedir, path)
+ local fspath = basedir..'/'..path
+ for name, dinfo in sio.opendir(fspath):iterate() do
+ local fssubpath = fspath..'/'..name
+ local subpath = path..'/'..name
+ --io.stderr:write("name:\t", subpath, '\n')
+ if name ~= '.' and name ~= '..' and whitelisted(subpath) then
+ dt = dinfo.d_type
+ dts = (dt == luxio.DT_REG and "Regular")
+ or (dt == luxio.DT_DIR and "Directory")
+ or (dt == luxio.DT_LNK and "Symlink")
+ --io.stderr:write("d_type:\t", dts, '\n')
+ local entry = archive.entry{
+ sourcepath = fssubpath,
+ pathname = localname..'/.git/'..subpath,
+ ino = 0,
+ dev = 0,
+ uid = 0,
+ uname = 'root',
+ gid = 0,
+ gname = 'root',
+ atime = {683074800, 683074800},
+ ctime = {683074800, 683074800},
+ mtime = {683074800, 683074800},
+ birthtime = {683074800, 683074800},
+ }
+ if dt == luxio.DT_REG then
+ writer:header(entry)
+ copy_file(fssubpath, function(bytes)
+ writer:data(bytes)
+ end)
+ elseif dt == luxio.DT_DIR then
+ writer:header(entry)
+ add_dir(writer, basedir, subpath)
+ elseif dt == luxio.DT_LNK then
+ writer:header(entry)
+ end
+ end
+ end
+end
+
+local function write_file(writer, path, data)
+ writer:header(archive.entry{
+ pathname = path,
+ mode = tonumber('100664', 8),
+ ino = 0,
+ dev = 0,
+ uid = 0,
+ uname = 'root',
+ gid = 0,
+ gname = 'root',
+ atime = {683074800, 683074800},
+ ctime = {683074800, 683074800},
+ mtime = {683074800, 683074800},
+ birthtime = {683074800, 683074800},
+ size = #data
+ })
+ writer:data(data)
+end
+
+local function run_git(dir, ...)
+ proc = sp.spawn_simple{cwd=dir, stdout=sp.PIPE, ...}
+ output = proc.stdout:read("*a")
+ how, why = proc:wait()
+ if how == -1 then
+ error("run_git failed: "..tostring(why))
+ end
+ return output
+end
+
+local writer = archive.write{
+ format = "pax",
+ writer = function(archive, string)
+ if string == nil then
+ -- pass
+ else
+ io.stdout:write(string)
+ return #string
+ end
+ end,
+}
+add_dir(writer, repo, '.')
+
+HEAD = run_git(repo, "git", "rev-parse",
+ "--abbrev-ref", "HEAD"):sub(1, -2)
+write_file(writer, localname..'/.git/HEAD',
+ 'ref: refs/heads/'..HEAD..'\n')
+config = get_config('git://git@gitserver/repo', HEAD)
+write_file(writer, localname..'/.git/config', config)
+
+for branch, sha1 in run_git(repo, 'git', 'for-each-ref',
+ '--format', '%(refname)%00%(objectname)%00',
+ 'refs/heads')
+ :gmatch"refs/heads/([^%z]+)%z([^%z]+)%z\n" do
+ write_file(writer, localname..'/.git/refs/remotes/origin/'..branch,
+ sha1..'\n')
+ if branch == HEAD then
+ write_file(writer, localname..'/.git/refs/heads/'..branch,
+ sha1..'\n')
+ end
+end