---- Git
+--- Generic git interface. Not to be confused with the git plugin which is
+-- only responsible for dealing with sources. Functions with '1' postfix
+-- take url strings as parameter. The others take server / location.
-- @module generic.generic_git
--[[
along with this program. If not, see <http://www.gnu.org/licenses/>.
]]
---- functions with '1' postfix take url strings as parameter. the others
--- take server / location
-
local generic_git = {}
local e2lib = require("e2lib")
local eio = require("eio")
local err = require("err")
local strict = require("strict")
+--- Trim off whitespaces and newline at the front and back of a string.
+-- @param str String to trim.
+-- @return Trimmed string.
+local function trim(str)
+ return string.match(str, "^%s*(.-)%s*$")
+end
+
--- Clone a git repository.
-- @param surl URL to the git repository (string).
-- @param destdir Destination on file system (string). Must not exist.
--- @param skip_checkout Pass -n to git clone? (boolean)
--- @return True on success, false on error
--- @return Error object on failure
+-- @param skip_checkout Pass -n to git clone? (boolean).
+-- @return True on success, false on error.
+-- @return Error object on failure.
local function git_clone_url(surl, destdir, skip_checkout)
- local rc, re
- local e = err.new("cloning git repository")
+ local rc, re, e, u, src, argv
+
+ e = err.new("cloning git repository")
- if (not surl) or (not destdir) then
+ if not surl or not destdir then
return false, err.new("git_clone_url(): missing parameter")
end
- local u, re = url.parse(surl)
+ u, re = url.parse(surl)
if not u then
return false, e:cat(re)
end
- local src, re = generic_git.git_url1(u)
+ src, re = generic_git.git_url1(u)
if not src then
return false, e:cat(re)
end
- local flags = ""
+ argv = { "clone" }
+
if skip_checkout then
- flags = "-n"
+ table.insert(argv, "-n")
end
- local cmd = string.format("git clone %s --quiet %s %s", flags,
- e2lib.shquote(src), e2lib.shquote(destdir))
+ table.insert(argv, "--quiet")
+ table.insert(argv, src)
+ table.insert(argv, destdir)
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
+ rc, re = generic_git.git(argv)
+ if not rc then
return false, e:cat(re)
end
- return true, nil
+ return true
end
---- git branch wrapper
--- @param gitwc string: path to the git repository
--- @param track bool: use --track or --no-track
--- @param branch string: name of the branch to create
--- @param start_point string: where to start the branch
--- @return bool
--- @return nil, an error object on failure
+--- Create argument vector for calling git.
+-- Defaults: If git_dir is given and the default for work_tree is requested,
+-- it's assumed to be one directory level up. If work_tree is given and the
+-- default for git_dir is requested, it's work_tree/.git. If neither are given,
+-- git_dir defaults to '.git' and work_tree to '.'.
+-- @param gitdir Git repository directory, nil for the default and false to omit.
+-- @param gitwc Git working copy directory, nil for the default and false to omit.
+-- @param ... Further arguments are added to the end of the argument vector.
+-- @return Argument vector table.
+local function git_new_argv2(git_dir, work_tree, ...)
+ local argv = {...}
+
+ if git_dir == nil and work_tree == nil then
+ e2lib.abort("git_new_argv2: git_dir == nil and work_tree == nil")
+ end
+
+ if git_dir == nil then
+ if work_tree then
+ git_dir = e2lib.join(work_tree, ".git")
+ else
+ git_dir = ".git"
+ end
+ end
+ if work_tree == nil then
+ if git_dir then
+ work_tree = e2lib.dirname(git_dir)
+ else
+ work_tree = "."
+ end
+ end
+
+ if work_tree then
+ table.insert(argv, 1, "--work-tree="..work_tree)
+ end
+ if git_dir then
+ table.insert(argv, 1, "--git-dir="..git_dir)
+ end
+
+ return argv
+end
+
+function generic_git.git_new_argv(git_dir, work_tree, ...)
+ return git_new_argv2(git_dir, work_tree, ...)
+end
+
+--- Call out to git. XXX: replace e2lib.git with this.
+-- @param argv Array of arguments to git.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+-- @return Any captured git output or the empty string if nothing was captured.
+function generic_git.git(argv)
+ local rc, re, e, git, cmd, fifo, out
+
+ git, re = tools.get_tool("git")
+ if not git then
+ return false, re
+ end
+
+ cmd = e2lib.shquote(git)
+ for _,arg in ipairs(argv) do
+ cmd = cmd .. " "
+ cmd = cmd .. e2lib.shquote(arg)
+ end
+
+ -- fifo contains the last 4 lines, out everything - it's simpler that way.
+ fifo = {}
+ out = {}
+
+ local function capture(msg)
+ if msg == "" then
+ return
+ end
+
+ if #fifo > 4 then
+ table.remove(fifo, 1)
+ end
+
+ e2lib.log(3, msg)
+ table.insert(fifo, msg)
+ table.insert(out, msg)
+ end
+
+ rc, re = e2lib.callcmd_capture(cmd, capture)
+ if not rc then
+ e = new.new("git command %q failed", cmd)
+ return false, e:cat(re), table.concat(out)
+ elseif rc ~= 0 then
+ e = err.new("git command %q failed with exit status %d", cmd, rc)
+ for _,v in ipairs(fifo) do
+ e:append("%s", v)
+ end
+ return false, e, table.concat(out)
+ end
+
+ return true, nil, table.concat(out)
+end
+
+--- Git branch wrapper. Sets up a branch, but does not switch to it.
+-- @param gitwc Path to the git repository.
+-- @param track Use --track if true, otherwise use --no-track.
+-- @param branch Name of the branch to create.
+-- @param start_point Start of the branch (string).
+-- @return True on success, false on error.
+-- @return Error object on failure.
function generic_git.git_branch_new1(gitwc, track, branch, start_point)
- local rc, re, e
- local f_track
- local cmd
+ local rc, re, e, argv
+
+ argv = git_new_argv2(nil, gitwc, "branch")
if track == true then
- f_track = "--track"
+ table.insert(argv, "--track")
else
- f_track = "--no-track"
+ table.insert(argv, "--no-track")
end
- -- git branch [--track|--no-track] <branch> <start_point>
- cmd = string.format( "cd %s && git branch %s %s %s",
- e2lib.shquote(gitwc), f_track, e2lib.shquote(branch),
- e2lib.shquote(start_point))
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
+ table.insert(argv, branch)
+ table.insert(argv, start_point)
+
+ rc, re = generic_git.git(argv)
+ if not rc then
e = err.new("creating new branch failed")
return false, e:cat(re)
end
return true
end
---- git checkout wrapper
--- @param gitwc string: path to the git repository
--- @param branch name of the branch to checkout
--- @return bool
--- @return an error object on failure
+--- Git checkout wrapper.
+-- @param gitwc Path to the git working directory.
+-- @param branch Branch name to check out.
+-- @return True on success, false on error.
+-- @return An error object on failure.
function generic_git.git_checkout1(gitwc, branch)
- local rc, re, e
- local cmd
-
- e2lib.logf(3, "checking out branch: %s", branch)
- cmd = string.format("cd %s && git checkout %s", e2lib.shquote(gitwc),
- e2lib.shquote(branch))
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
+ local rc, re, e, argv
+
+ argv = git_new_argv2(nil, gitwc, "checkout", branch)
+ rc, re = generic_git.git(argv)
+ if not rc then
e = err.new("git checkout failed")
return false, e:cat(re)
end
return true
end
---- git rev-list wrapper function
--- @param gitdir string: GIT_DIR
--- @param ref string: a reference, according to the git manual
--- @return string: the commit id matching the ref parameter, or nil on error
--- @return an error object on failure
+--- Git rev-list wrapper.
+-- @param gitdir Path to the git repository directory (GIT_DIR).
+-- @param ref Git reference, see git-rev-list(1).
+-- @return True on success, false on error.
+-- @return Error object on failure
+-- @return Hexadecimal commit ID string or empty string if there was no match.
function generic_git.git_rev_list1(gitdir, ref)
- local e = err.new("git rev-list failed")
- local rc, re
+ local e, rc, re, argv, id
- local tmpfile, re = e2lib.mktempfile()
- if not tmpfile then
- return false, e:cat(re)
- end
+ e = err.new("git rev-list failed")
- local args = string.format("--max-count=1 '%s' -- >'%s'", ref, tmpfile)
- rc, re = e2lib.git(gitdir, "rev-list", args)
+ argv = git_new_argv2(gitdir, nil, "rev-list", "--max-count=1", ref, "--")
+
+ rc, re, id = generic_git.git(argv)
if not rc then
- return false, e -- do not include the low-level error here
- end
- local f, msg = io.open(tmpfile, "r")
- if not f then
- return nil, e:cat(msg)
- end
- local rev = f:read()
- f:close()
- e2lib.rmtempfile(tmpfile)
- if (not rev) or (not rev:match("^%S+$")) then
- return nil, err.new("can't parse git rev-list output")
+ return false, e:cat(re)
end
- if rev then
- e2lib.logf(4, "git_rev_list: %s", rev)
- else
- e2lib.logf(4, "git_rev_list: unknown ref: %s", ref)
+
+ id = trim(id)
+ if string.match(id, "^%x+$") or id == "" then
+ return true, nil, id
end
- return rev, nil
+
+ return false, err.new("can't parse git rev-list output")
end
---- initialize a git repository
--- @param rurl string: remote url
--- @return bool
--- @return an error object on failure
-function generic_git.git_init_db1(rurl)
- local rc, re, e
- local u
- local gitdir, gitcmd
- local cmd
+--- Initialize a git repository.
+-- @param rurl URL string where the repository should be created.
+-- @param shared Should the repository be shared with other users or not.
+-- Defaults to true.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function generic_git.git_init_db1(rurl, shared)
+ local rc, re, e, u, gitdir, gitargv, argv
+
+ -- XXX: Remove the default behaviour and require a choice
+ if shared == nil then
+ shared = true
+ end
- if (not rurl) then
+ if not rurl then
return false, err.new("git_init_db1(): missing parameter")
end
end
gitdir = e2lib.join("/", u.path);
- gitcmd = string.format("mkdir -p %s && GIT_DIR=%s git init-db --shared",
- e2lib.shquote(gitdir), e2lib.shquote(gitdir))
+ gitargv = { "--git-dir="..gitdir, "init-db" }
+ if shared then
+ table.insert(gitargv, "--shared")
+ end
if u.transport == "ssh" or u.transport == "scp" or
u.transport == "rsync+ssh" then
- local ssh, re = tools.get_tool("ssh")
- if not ssh then
- return false, re
+
+ argv = { "mkdir", "-p", gitdir }
+ rc, re = e2lib.ssh_remote_cmd(u, argv)
+ if not rc then
+ return false, e:cat(re)
+ end
+
+ table.insert(gitargv, 1, "git")
+ rc, re = e2lib.ssh_remote_cmd(u, gitargv)
+ if not rc then
+ return false, e:cat(re)
end
- cmd = string.format("%s %s %s", e2lib.shquote(ssh),
- e2lib.shquote(u.server), e2lib.shquote(gitcmd))
elseif u.transport == "file" then
- cmd = gitcmd
- else
- return false, err.new("git_init_db: can not initialize git repository"..
- " on this transport: %s", u.transport)
- end
+ rc, re = e2lib.mkdir_recursive(gitdir)
+ if not rc then
+ return false, e:cat(re)
+ end
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
- return false, e:cat(re)
+ rc, re = generic_git.git(gitargv)
+ if not rc then
+ return false, e:cat(re)
+ end
+ else
+ return false, err.new("git_init_db: initializing git repository"..
+ " on transport %q is not supported", u.transport)
end
return true
end
---- do a git push
--- @param gitdir string: absolute path to a gitdir
--- @param rurl string: remote url
+--- Git push.
+-- @param gitdir Absolute path to git-dir
+-- @param rurl URL string to repository.
-- @param refspec string: a git refspec
--- @return bool
--- @return an error object on failure
+-- @return True on success, false on error.
+-- @return Error object on failure.
function generic_git.git_push1(gitdir, rurl, refspec)
- local rc, re, e, u, remote_git_url, cmd
+ local rc, re, e, u, remote_git_url, argv
- if (not rurl) or (not gitdir) or (not refspec) then
+ if not rurl or not gitdir or not refspec then
return false, err.new("git_push1(): missing parameter")
end
return false, e:cat(re)
end
- -- GIT_DIR=gitdir git push remote_git_url refspec
- cmd = string.format("GIT_DIR=%s git push %s %s", e2lib.shquote(gitdir),
- e2lib.shquote(remote_git_url), e2lib.shquote(refspec))
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
+ argv = { "--git-dir=" .. gitdir, "push", remote_git_url, refspec }
+ rc, re = generic_git.git(argv)
+ if not rc then
return false, e:cat(re)
end
return true
end
---- do a git remote-add
+--- Git remote add. XXX: local to generic_git
-- @param lurl string: local git repo
-- @param rurl string: remote url
-- @param name string: remote name
--- @return bool
--- @return an error object on failure
+-- @return True on success, false on error.
+-- @return Error object on failure.
function generic_git.git_remote_add1(lurl, rurl, name)
- local rc, re, e, lrepo, rrepo, giturl, cmd
+ local rc, re, e, lrepo, rrepo, giturl, gitdir, argv
- if (not lurl) or (not rurl) or (not name) then
- return false, err.new("missing parameter")
+ if not lurl or not rurl or not name then
+ return false, err.new("git_remote_add1: missing parameter")
end
e = err.new("git remote-add failed")
if not giturl then
return false, e:cat(re)
end
- -- git remote add <name> <giturl>
- cmd = string.format("cd %s && git remote add %s %s",
- e2lib.shquote("/"..lrepo.path), e2lib.shquote(name),
- e2lib.shquote(giturl))
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
- return false, e:cat(re)
+
+ gitdir = e2lib.join("/", lrepo.path)
+ argv = { "--git-dir="..gitdir, "remote", "add", name, giturl }
+
+ rc, re = generic_git.git(argv)
+ if not rc then
+ return false, re
end
return true
end
---- Add git remote.
+--- Add git remote. XXX: Local to to generic_git
-- @return True on success, false on error.
-- @return Error object on failure.
function generic_git.git_remote_add(c, lserver, llocation, name, rserver, rlocation)
return true
end
---- translate a url to a git url
--- @param u url table
--- @return string: the git url
--- @return an error object on failure
+--- Generate git URL string from URL object.
+-- @param u URL object
+-- @return Git URL string or false on error.
+-- @return Error object on failure.
function generic_git.git_url1(u)
local giturl
+
if u.transport == "ssh" or u.transport == "scp" or
u.transport == "rsync+ssh" then
giturl = string.format("git+ssh://%s/%s", u.server, u.path)
u.transport == "git" then
giturl = string.format("%s://%s/%s", u.transport, u.server, u.path)
else
- return nil, err.new("git_url1: transport not supported: %s", u.transport)
+ return false,
+ err.new("git_url1: transport not supported: %s", u.transport)
end
- return giturl, nil
+
+ return giturl
end
--- clone a git repository by server and location
return generic_git.git_push1(gitdir, rurl, refspec)
end
---- do a git config query
+--- Git config query. XXX: used only in git plugin
-- @param gitdir string: gitdir
-- @param query string: query to pass to git config
--- @return string: the value printed to stdout by git config, or nil
--- @return an error object on failure
+-- @return Value printed to stdout by git config, or false on error.
+-- @return Error object on failure.
function generic_git.git_config(gitdir, query)
- local rc, re
- local e = err.new("running git config")
+ local e, rc, re, argv, out
- local tmpfile, re = e2lib.mktempfile()
- if not tmpfile then
+ argv = git_new_argv2(gitdir, false, "config", query)
+ rc, re, out = generic_git.git(argv)
+ if not rc then
+ e = err.new("git config failed")
return false, e:cat(re)
end
- local cmd = string.format("GIT_DIR=%s git config %s > %s",
- e2lib.shquote(gitdir), e2lib.shquote(query), e2lib.shquote(tmpfile))
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
- e:append("git config failed")
- return nil, e
- end
- local git_output, re = eio.file_read_line(tmpfile)
- if not git_output then
- e:cat(re)
- return nil, e:append("can't read git output from temporary file")
- end
- e2lib.rmtempfile(tmpfile)
- return git_output, nil
+ return trim(out)
end
---- do a git add
--- @param gitdir string: gitdir (optional, default: .git)
--- @param args string: args to pass to git add
--- @return bool
--- @return an error object on failure
-function generic_git.git_add(gitdir, args)
- local rc, re
- local e = err.new("running git add")
- if not gitdir then
- gitdir = ".git"
- end
- local cmd = string.format("GIT_DIR=%s git add %s",
- e2lib.shquote(gitdir), e2lib.shquote(args))
- rc, re = e2lib.callcmd_log(cmd)
- if not rc or rc ~= 0 then
- return nil, e:cat(re)
+--- Git add.
+-- @param gitdir Path to GIT_DIR.
+-- @param argv Argument vector to pass to git add, usually the files to add.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function generic_git.git_add(gitdir, argv)
+ local rc, re, e, v
+
+ v = git_new_argv2(gitdir, nil, "add", unpack(argv))
+
+ rc, re = generic_git.git(v)
+ if not rc then
+ e = err.new("git add failed")
+ return false, e:cat(re)
end
- return true, nil
+
+ return true
end
---- do a git commit
--- @param gitdir string: gitdir (optional, default: .git)
--- @param args string: args to pass to git add
--- @return bool
--- @return an error object on failure
-function generic_git.git_commit(gitdir, args)
- local rc, re
- local e = err.new("git commit failed")
- return e2lib.git("commit", gitdir, args)
+--- Git commit.
+-- @param gitdir Path to GIT_DIR.
+-- @param argv Argument vector to pass to git commit, like a comment.
+-- @return True on success, false on error.
+-- @return Error object on failure.
+function generic_git.git_commit(gitdir, argv)
+ local e, rc, re, v
+
+ v = git_new_argv2(gitdir, false, "commit", unpack(argv))
+
+ rc, re = generic_git.git(v)
+ if not rc then
+ e = err.new("git commit failed")
+ return false, e:cat(re)
+ end
+
+ return true
end
---- compare a local tag and the remote tag with the same name
--- @param gitdir string: gitdir (optional, default: .git)
--- @param tag string: tag name
--- @return bool, or nil on error
--- @return an error object on failure
+--- Check local tag and the remote tag point to the same commit.
+-- @param gitdir Path to GIT_DIR.
+-- @param tag Git tag name.
+-- @return True on success, false on error or mismatch.
+-- @return Error object on failure.
function generic_git.verify_remote_tag(gitdir, tag)
local e = err.new("verifying remote tag")
- local rc, re
+ local rc, re, rtag, argv
- -- fetch the remote tag
- -- TODO: escape args, tag, rtag
- local rtag = string.format("%s.remote", tag)
- local args = string.format("origin refs/tags/%s:refs/tags/%s",
- tag, rtag)
- rc, re = e2lib.git(gitdir, "fetch", args)
+ -- temporary local name for remote tag
+ rtag = string.format("%s.remote", tag)
+
+ -- fetch the remote tag as rtag
+ argv = git_new_argv2(gitdir, false, "fetch", "origin")
+ table.insert(argv, string.format("refs/tags/%s:refs/tags/%s", tag, rtag))
+
+ rc, re = generic_git.git(argv)
if not rc then
- return false, err.new("remote tag is not available: %s", tag)
+ e = err.new("remote tag %q is not available", tag)
+ return false, e:cat(re)
end
- -- store commit ids for use in the error message, if any
- local lrev = generic_git.git_rev_list1(gitdir, tag)
- if not lrev then
- return nil, e:cat(re)
- end
- local rrev = generic_git.git_rev_list1(gitdir, rtag)
- if not rrev then
- return nil, e:cat(re)
+ -- before leaving, remove the temporary rtag
+ local function cleanup_rtag(gitdir, rtag)
+ local argv = git_new_argv2(gitdir, false, "tag", "-d", rtag)
+ generic_git.git(argv)
end
- -- check that local and remote tags point to the same revision
- local args = string.format("--quiet '%s' '%s'", rtag, tag)
- local equal, re = e2lib.git(gitdir, "diff", args)
+ local rc, re, lid = generic_git.git_rev_list1(gitdir, tag)
+ if not rc then
+ cleanup_rtag(rtag)
+ return false, e:cat(re)
+ elseif lid == "" then
+ cleanup_rtag(rtag)
+ re = err.new("can not find commit ID for local tag %q in %q",
+ gitdir, tag)
+ return false, e:cat(re)
+ end
- -- delete the remote tag again, before evaluating the return code
- -- of 'git diff'
- local args = string.format("-d '%s'", rtag)
- rc, re = e2lib.git(gitdir, "tag", args)
+ local rc, re, rid = generic_git.git_rev_list1(gitdir, rtag)
if not rc then
- return nil, e:cat(re)
+ cleanup_rtag(rtag)
+ return false, e:cat(re)
+ elseif rid == "" then
+ cleanup_rtag(rtag)
+ re = err.new("can not find commit ID for remote tag %q in %q",
+ gitdir, tag --[[ not rtag]])
+ return false, e:cat(re)
end
- if not equal then
+
+ cleanup_rtag(rtag)
+
+ if lid ~= rid then
return false, e:append(
"local tag differs from remote tag\n"..
"tag name: %s\n"..
"local: %s\n"..
- "remote: %s\n", tag, lrev, rrev)
+ "remote: %s\n", tag, lid, rid)
end
- return true, nil
+
+ return true
end
---- verify that the working copy is clean and matches HEAD
--- @param gitwc string: path to a git working tree (default: .)
--- @return bool, or nil on error
--- @return an error object on failure
+--- Verify that the working copy is clean and matches HEAD. XXX: only in e2tool
+-- @param gitwc Path to git working tree.
+-- @return True if the working copy is clean, false on any error.
+-- @return Error object on failure.
+-- @return True if the error is because the work tree is dirty.
function generic_git.verify_clean_repository(gitwc)
- gitwc = gitwc or "."
- local e = err.new("verifying that repository is clean")
- local rc, re
+ local e, rc, re, out, argv, files
- local tmp, re = e2lib.mktempfile()
- if not tmp then
- return false, e:cat(re)
- end
+ e = err.new("verifying that repository is clean failed")
- rc, re = e2lib.chdir(gitwc)
- if not rc then
- return nil, e:cat(re)
- end
-- check for unknown files in the filesystem
- local args = string.format(
- "--exclude-standard --directory --others >%s", tmp)
- rc, re = e2lib.git(nil, "ls-files", args)
+ argv = git_new_argv2(nil, gitwc, "ls-files", "--exclude-standard",
+ "--directory", "--others")
+
+ rc, re, files = generic_git.git(argv)
if not rc then
- return nil, e:cat(re)
- end
- local x, msg = io.open(tmp, "r")
- if not x then
- return nil, e:cat(msg)
+ return false, e:cat(re)
end
- local files = x:read("*a")
- x:close()
- if #files > 0 then
- local msg = "the following files are not checked into the repository:\n"
- msg = msg .. files
- return false, err.new("%s", msg)
+
+ if string.len(files) > 0 then
+ re = err.new("the following files are not checked in:\n%s", files)
+ return false, e:cat(re), true
end
+
-- verify that the working copy matches HEAD
- local args = string.format("--name-only HEAD >%s", tmp)
- rc, re = e2lib.git(nil, "diff-index", args)
+ argv = git_new_argv2(nil, gitwc, "diff-index", "--name-only", "HEAD")
+
+ rc, re, files = generic_git.git(argv)
if not rc then
- return nil, e:cat(re)
- end
- local x, msg = io.open(tmp, "r")
- if not x then
- return nil, e:cat(msg)
+ return false, e:cat(re)
end
- local files = x:read("*a")
- x:close()
- if #files > 0 then
- msg = "the following files are modified:\n"
- msg = msg..files
- return false, err.new("%s", msg)
+
+ if string.len(files) > 0 then
+ re = err.new("the following files are modified:\n%s", files)
+ return false, e:cat(re), true
end
+
-- verify that the index matches HEAD
- local args = string.format("--name-only --cached HEAD >%s", tmp)
- rc, re = e2lib.git(nil, "diff-index", args)
+ argv = git_new_argv2(nil, gitwc, "diff-index", "--name-only",
+ "--cached", "HEAD")
+ rc, re, files = generic_git.git(argv)
if not rc then
- return nil, e:cat(re)
- end
- local x, msg = io.open(tmp, "r")
- if not x then
- return nil, e:cat(msg)
+ return false, e:cat(re)
end
- local files = x:read("*a")
- x:close()
- if #files > 0 then
- msg = "the following files in index are modified:\n"
- msg = msg..files
- return false, err.new("%s", msg)
+
+ if string.len(files) > 0 then
+ re =
+ err.new("the following files in the index are modified:\n%s", files)
+ return false, e:cat(re), true
end
+
return true
end
---- verify that HEAD matches the given tag
--- @param gitwc string: gitdir (optional, default: .git)
--- @param verify_tag string: tag name
--- @return bool, or nil on error
--- @return an error object on failure
+--- verify that HEAD matches the given tag. XXX: only used in e2tool
+-- @param gitwc Path to git work tree.
+-- @param verify_tag Git tag to verify.
+-- @return True on success, false on error and mismatch.
+-- @return Error object on failure.
+-- @return True on mismatch.
function generic_git.verify_head_match_tag(gitwc, verify_tag)
- assert(verify_tag)
- gitwc = gitwc or "."
- local e = err.new("verifying that HEAD matches 'refs/tags/%s'", verify_tag)
- local rc, re
+ local rc, re, e, argv, tag
- local tmp, re = e2lib.mktempfile()
- if not tmp then
- return false, e:cat(re)
- end
+ e = err.new("verifying that HEAD matches 'refs/tags/%s'", verify_tag)
- local args = string.format("--tags --match '%s' >%s", verify_tag, tmp)
- rc, re = e2lib.chdir(gitwc)
- if not rc then
- return nil, e:cat(re)
- end
- rc, re = e2lib.git(nil, "describe", args)
+ argv = git_new_argv2(nil, gitwc, "describe", "--tags",
+ "--match", verify_tag)
+
+ rc, re, tag = generic_git.git(argv)
if not rc then
- return nil, e:cat(re)
- end
- local x, msg = io.open(tmp, "r")
- if not x then
- return nil, e:cat(msg)
- end
- local tag, msg = x:read()
- x:close()
- if tag == nil then
- return nil, e:cat(msg)
+ return false, e:cat(re)
end
+
+ tag = trim(tag)
if tag ~= verify_tag then
- return false
+ return false, err.new("tag %q does not match expected tag %q",
+ tag, verify_tag), true
end
+
return true
end
+---
function generic_git.sourceset2ref(sourceset, branch, tag)
if sourceset == "branch" or
(sourceset == "lazytag" and tag == "^") then
return nil, "invalid sourceset"
end
---- create a new git source repository
+--- Create a new git source repository. XXX: only used in e2-new-source
-- @param c cache table
-- @param lserver string: local server
-- @param llocation string: working copy location on local server
-- @param rserver string: remote server
-- @param rlocation string: repository location on remote server
--- @param flags table of flags
--- @return bool
--- @return nil, or an error string on error
+-- @param flags table of flags XXX: unused
+-- @return True on success, false on error.
+-- @return Error object on failure.
function generic_git.new_repository(c, lserver, llocation, rserver, rlocation, flags)
- local rc, re
- local e = err.new("setting up new git repository failed")
- local lserver_url, re = cache.remote_url(c, lserver, llocation)
+ local rc, re, e, lserver_url, lurl, targs, gitdir, argv
+
+ e = err.new("setting up new git repository failed")
+
+ lserver_url, re = cache.remote_url(c, lserver, llocation)
if not lserver_url then
return false, e:cat(re)
end
- local lurl, re = url.parse(lserver_url)
+
+ lurl, re = url.parse(lserver_url)
if not lurl then
return false, e:cat(re)
end
- local rc = e2lib.mkdir_recursive(e2lib.join("/", lurl.path))
+
+ gitdir = e2lib.join("/", lurl.path)
+ rc, re = e2lib.mkdir_recursive(gitdir)
if not rc then
- return false, e:cat("can't create path to local git repository")
+ e:cat("can't create path to local git repository")
+ return false, e:cat(re)
end
- rc = generic_git.git_init_db(c, lserver, llocation)
+
+ rc, re = generic_git.git_init_db(c, lserver, llocation)
if not rc then
- return false, e:cat("can't initialize local git repository")
+ e:cat("can't initialize local git repository")
+ return false, e:cat(re)
end
- rc = generic_git.git_remote_add(c, lserver, llocation, "origin",
- rserver, rlocation)
+
+ rc, re = generic_git.git_remote_add(c, lserver, llocation, "origin", rserver, rlocation)
if not rc then
- return false, e:cat("git remote add failed")
+ return false, e:cat(re)
end
- rc = e2lib.chdir("/"..lurl.path)
+
+ argv = { "--git-dir="..gitdir, "config", "branch.master.remote", "origin" }
+ rc, re = generic_git.git(argv)
if not rc then
return false, e:cat(re)
end
- local targs = {
- string.format("'branch.master.remote' 'origin'"),
- string.format("'branch.master.merge' 'refs/heads/master'"),
- }
- for _,args in ipairs(targs) do
- rc, re = e2lib.git(".", "config", args)
- if not rc then
- return false, e:cat(re)
- end
+
+ argv = { "--git-dir="..gitdir, "config", "branch.master.merge",
+ "refs/heads/master" }
+ rc, re = generic_git.git(argv)
+ if not rc then
+ return false, e:cat(re)
end
+
rc, re = generic_git.git_init_db(c, rserver, rlocation)
if not rc then
return false, e:cat(re)
end
- return true, nil
+
+ return true
end
return strict.lock(generic_git)